├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── README.md
├── VoiceManager-master.iml
├── VoiceManager.iml
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── jaydenxiao
│ │ └── voicemanager
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── jaydenxiao
│ │ └── voicemanager
│ │ ├── Arith.java
│ │ ├── BaseCommonAdapter.java
│ │ ├── MainActivity.java
│ │ ├── RecordVoiceButton.java
│ │ ├── ViewHolderUtil.java
│ │ ├── Voice.java
│ │ ├── VoiceAdapter.java
│ │ ├── VoiceLineView.java
│ │ ├── VoiceManager.java
│ │ └── VoiceTimeUtils.java
│ └── res
│ ├── drawable-hdpi
│ ├── icon_complete.png
│ ├── icon_continue.png
│ ├── icon_pause.png
│ ├── icon_record.png
│ ├── icon_voice_record.png
│ ├── voicetip_play_1.png
│ ├── voicetip_play_2.png
│ └── voicetip_play_3.png
│ ├── drawable
│ ├── playbar_style.xml
│ ├── round_background.xml
│ ├── round_selector.xml
│ └── voice_receive.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── dialog_record_voice.xml
│ └── item_voice_list.xml
│ ├── menu
│ └── menu_main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── art
├── Screenshot_2017-03-21-17-28-01.png
├── Screenshot_2017-03-21-17-28-05.png
├── Screenshot_2017-03-21-17-28-26.png
└── art.mp4
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── hisign.html
├── settings.gradle
└── video_20180702_130810.mp4
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/workspace.xml
4 | /.idea/libraries
5 | .DS_Store
6 | /build
7 | /captures
8 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | VoiceManager
2 | ============
3 | 1.实现录音功能,提供暂停、继续切换,并且监听音量大小以波浪线呈现出现
4 |
5 | 2.实现播放功能,播放实现帧动画
6 |
7 | 用法:
8 |
9 | 1.录音
10 |
11 | VoiceManage mVoiceManage =VoiceManager.getInstance(mContext);
12 |
13 | mVoiceManage.pauseOrStartVoiceRecord();//暂停或继续
14 |
15 | mVoiceManage.stopVoiceRecord();//完成录音
16 |
17 | mVoiceManage.setVoiceRecordListener(new VoiceManager.VoiceRecordCallBack() {
18 | @Override
19 | public void recDoing(long time, String strTime) {
20 | mRecordHintTv.setText(strTime);
21 | }
22 |
23 | @Override
24 | public void recVoiceGrade(int grade) {
25 | voicLine.setVolume(grade);
26 | }
27 |
28 | @Override
29 | public void recStart(boolean init) {
30 | mIvPauseContinue.setImageResource(R.drawable.icon_pause);
31 | voicLine.setContinue();
32 | }
33 |
34 | @Override
35 | public void recPause(String str) {
36 | mIvPauseContinue.setImageResource(R.drawable.icon_continue);
37 | voicLine.setPause();
38 | }
39 |
40 |
41 | @Override
42 | public void recFinish(long length, String strLength, String path) {
43 | if (enRecordVoiceListener != null) {
44 | enRecordVoiceListener.onFinishRecord(length, strLength, path);
45 | }
46 | }
47 | });
48 |
49 | 2.播放
50 | VoiceManage mVoiceManage =VoiceManager.getInstance(mContext);
51 | mVoiceManage.setVoicePlayListener(new VoiceManager.VoicePlayCallBack() {
52 | @Override
53 | public void voiceTotalLength(long time, String strTime) {
54 |
55 | }
56 |
57 | @Override
58 | public void playDoing(long time, String strTime) {
59 |
60 |
61 | }
62 |
63 | @Override
64 | public void playPause() {
65 |
66 | }
67 |
68 | @Override
69 | public void playStart() {
70 |
71 | }
72 |
73 | @Override
74 | public void playFinish() {
75 | if (voiceAnimation != null) {
76 | voiceAnimation.stop();
77 | voiceAnimation.selectDrawable(0);
78 | }
79 | }
80 | });
81 | mVoiceManage.startPlay(voice.getFilePath());
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | License
92 | =======
93 |
94 | The MIT License (MIT)
95 |
96 | Copyright (c) 2017 jaydenxiao2016
97 |
98 | Permission is hereby granted, free of charge, to any person obtaining a copy
99 | of this software and associated documentation files (the "Software"), to deal
100 | in the Software without restriction, including without limitation the rights
101 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
102 | copies of the Software, and to permit persons to whom the Software is
103 | furnished to do so, subject to the following conditions:
104 |
105 | The above copyright notice and this permission notice shall be included in all
106 | copies or substantial portions of the Software.
107 |
108 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
109 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
110 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
111 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
112 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
113 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
114 | SOFTWARE.
115 |
--------------------------------------------------------------------------------
/VoiceManager-master.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/VoiceManager.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.1"
6 |
7 | defaultConfig {
8 | applicationId "com.jaydenxiao.voicemanager"
9 | minSdkVersion 15
10 | targetSdkVersion 25
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:25.1.0'
25 | }
26 |
--------------------------------------------------------------------------------
/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/youzehong/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/jaydenxiao/voicemanager/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
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 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/Arith.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 | import java.math.BigDecimal;
4 |
5 | /**
6 | * Created by carlos on 2016/4/5.
7 | */
8 | public class Arith{
9 |
10 | //默认除法运算精度
11 | private static final int DEF_DIV_SCALE = 10;
12 | //这个类不能实例化
13 | private Arith(){
14 | }
15 |
16 | /**
17 | * 提供精确的加法运算。
18 | * @param v1 被加数
19 | * @param v2 加数
20 | * @return 两个参数的和
21 | */
22 | public static double add(double v1,double v2){
23 | BigDecimal b1 = new BigDecimal(Double.toString(v1));
24 | BigDecimal b2 = new BigDecimal(Double.toString(v2));
25 | return b1.add(b2).doubleValue();
26 | }
27 | /**
28 | * 提供精确的减法运算。
29 | * @param v1 被减数
30 | * @param v2 减数
31 | * @return 两个参数的差
32 | */
33 | public static double sub(double v1,double v2){
34 | BigDecimal b1 = new BigDecimal(Double.toString(v1));
35 | BigDecimal b2 = new BigDecimal(Double.toString(v2));
36 | return b1.subtract(b2).doubleValue();
37 | }
38 | /**
39 | * 提供精确的乘法运算。
40 | * @param v1 被乘数
41 | * @param v2 乘数
42 | * @return 两个参数的积
43 | */
44 | public static double mul(double v1,double v2){
45 | BigDecimal b1 = new BigDecimal(Double.toString(v1));
46 | BigDecimal b2 = new BigDecimal(Double.toString(v2));
47 | return b1.multiply(b2).doubleValue();
48 | }
49 |
50 | /**
51 | * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
52 | * 小数点以后10位,以后的数字四舍五入。
53 | * @param v1 被除数
54 | * @param v2 除数
55 | * @return 两个参数的商
56 | */
57 | public static double div(double v1,double v2){
58 | return div(v1,v2,DEF_DIV_SCALE);
59 | }
60 |
61 | /**
62 | * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
63 | * 定精度,以后的数字四舍五入。
64 | * @param v1 被除数
65 | * @param v2 除数
66 | * @param scale 表示表示需要精确到小数点以后几位。
67 | * @return 两个参数的商
68 | */
69 | public static double div(double v1,double v2,int scale){
70 | if(scale<0){
71 | throw new IllegalArgumentException(
72 | "The scale must be a positive integer or zero");
73 | }
74 | BigDecimal b1 = new BigDecimal(Double.toString(v1));
75 | BigDecimal b2 = new BigDecimal(Double.toString(v2));
76 | return b1.divide(b2,scale, BigDecimal.ROUND_HALF_UP).doubleValue();
77 | }
78 |
79 | /**
80 | * 提供精确的小数位四舍五入处理。
81 | * @param v 需要四舍五入的数字
82 | * @param scale 小数点后保留几位
83 | * @return 四舍五入后的结果
84 | */
85 | public static double round(double v,int scale){
86 |
87 | if(scale<0){
88 | throw new IllegalArgumentException(
89 | "The scale must be a positive integer or zero");
90 | }
91 | BigDecimal b = new BigDecimal(Double.toString(v));
92 | BigDecimal one = new BigDecimal("1");
93 | return b.divide(one,scale, BigDecimal.ROUND_HALF_UP).doubleValue();
94 | }
95 | };
96 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/BaseCommonAdapter.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 | import android.content.Context;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * 类名:BaseCommonAdapter.java
13 | * 作者:xsf
14 | * 描述:base适配器
15 | */
16 | public class BaseCommonAdapter extends android.widget.BaseAdapter {
17 |
18 | protected Context mContext;
19 | protected LayoutInflater mInflater;
20 | protected List data;
21 |
22 | public BaseCommonAdapter(Context context) {
23 | super();
24 | mContext = context;
25 | mInflater = LayoutInflater.from(mContext);
26 | this.data = new ArrayList<>();
27 | }
28 |
29 | public BaseCommonAdapter(Context context, List data) {
30 | super();
31 | mContext = context;
32 | if (data != null) {
33 | this.data = data;
34 | }
35 | else {
36 | this.data = new ArrayList<>();
37 | }
38 | mInflater = LayoutInflater.from(mContext);
39 | }
40 | /**
41 | * 设置数据源
42 | *
43 | * @param d
44 | */
45 | public void setData(List d) {
46 | data = d;
47 | }
48 |
49 | /**
50 | * 获取数据源
51 | *
52 | * @return
53 | */
54 | public List getData() {
55 | return data;
56 | }
57 |
58 | /**
59 | * 替换数据源
60 | *
61 | * @param d
62 | */
63 | public void replaceAllData(List d) {
64 | if (d == null) return;
65 | data.clear();
66 | data.addAll(d);
67 | notifyDataSetChanged();
68 | }
69 |
70 | /**
71 | * 增加单个数据
72 | *
73 | * @param d
74 | */
75 | public void add(T d) {
76 | if (d == null) return;
77 | if (data == null) data = new ArrayList<>();
78 | data.add(d);
79 | notifyDataSetChanged();
80 | }
81 |
82 | /**
83 | * 增加数据源
84 | *
85 | * @param d
86 | */
87 | public void addAll(List d) {
88 | if (d == null) return;
89 | if (data == null) data = new ArrayList<>();
90 | data.addAll(d);
91 | notifyDataSetChanged();
92 | }
93 |
94 | /**
95 | * 在某个位置添加数据源
96 | *
97 | * @param position
98 | * @param d
99 | */
100 | public void addAllAt(int position, List d) {
101 | if (d == null) return;
102 | if (data == null) data = new ArrayList<>();
103 | data.addAll(position, d);
104 | notifyDataSetChanged();
105 | }
106 |
107 | /**
108 | * 在某个位置添加单个数据源
109 | *
110 | * @param position
111 | * @param d
112 | */
113 | public void addAt(int position, T d) {
114 | if (d == null) return;
115 | if (data == null) data = new ArrayList<>();
116 | data.add(position, d);
117 | notifyDataSetChanged();
118 | }
119 |
120 | /**
121 | * 移除某个数据源
122 | *
123 | * @param position
124 | */
125 | public void remove(int position) {
126 | if (data == null) return;
127 | data.remove(position);
128 | notifyDataSetChanged();
129 | }
130 |
131 | /**
132 | * 清空所有数据
133 | */
134 | public void clearAll() {
135 | data.clear();
136 | }
137 |
138 | @Override
139 | public int getCount() {
140 | return data.size();
141 | }
142 |
143 | @Override
144 | public T getItem(int position) {
145 | return data.get(position);
146 | }
147 |
148 | @Override
149 | public long getItemId(int position) {
150 | return 0;
151 | }
152 |
153 | @Override
154 | public View getView(int position, View convertView, ViewGroup parent) {
155 | return null;
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.widget.ListView;
6 |
7 | /**
8 | * 作者:xsf
9 | */
10 | public class MainActivity extends AppCompatActivity {
11 |
12 | /**
13 | * 语音列表
14 | */
15 | private ListView listView;
16 | /**
17 | * 开始录音
18 | */
19 | private VoiceAdapter adapter;
20 | private RecordVoiceButton mBtRec;
21 |
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_main);
27 |
28 | mBtRec = (RecordVoiceButton) findViewById(R.id.button_rec);
29 | listView = (ListView) findViewById(R.id.lv);
30 | adapter=new VoiceAdapter(this);
31 | listView.setAdapter(adapter);
32 | mBtRec.setEnrecordVoiceListener(new RecordVoiceButton.EnRecordVoiceListener() {
33 | @Override
34 | public void onFinishRecord(long length, String strLength, String filePath) {
35 | adapter.add(new Voice(length, strLength, filePath) ) ;
36 | }
37 | });
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/RecordVoiceButton.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 |
4 | import android.app.Dialog;
5 | import android.content.Context;
6 | import android.os.Environment;
7 | import android.util.AttributeSet;
8 | import android.view.View;
9 | import android.widget.Button;
10 | import android.widget.ImageView;
11 | import android.widget.TextView;
12 |
13 | /**
14 | * 录音控件button
15 | */
16 | public class RecordVoiceButton extends Button implements View.OnClickListener {
17 |
18 | private Dialog recordIndicator;
19 | private ImageView mVolumeIv,mIvPauseContinue,mIvComplete;
20 | private VoiceLineView voicLine;
21 | private TextView mRecordHintTv;
22 | private Context mContext;
23 | private EnRecordVoiceListener enRecordVoiceListener;
24 | private VoiceManager voiceManager;
25 |
26 | public RecordVoiceButton(Context context) {
27 | super(context);
28 | init();
29 | }
30 |
31 | public RecordVoiceButton(Context context, AttributeSet attrs, int defStyle) {
32 | super(context, attrs, defStyle);
33 | this.mContext = context;
34 | init();
35 | }
36 |
37 | public RecordVoiceButton(Context context, AttributeSet attrs) {
38 | super(context, attrs);
39 | this.mContext = context;
40 | init();
41 | }
42 |
43 | private void init() {
44 | voiceManager =VoiceManager.getInstance(mContext);
45 | setOnClickListener(this);
46 | }
47 |
48 | /**
49 | * 设置监听
50 | *
51 | * @param enRecordVoiceListener
52 | */
53 | public void setEnrecordVoiceListener(EnRecordVoiceListener enRecordVoiceListener) {
54 | this.enRecordVoiceListener = enRecordVoiceListener;
55 | }
56 |
57 | /**
58 | * 启动录音dialog
59 | */
60 | private void startRecordDialog() {
61 | recordIndicator = new Dialog(getContext(), R.style.record_voice_dialog);
62 | recordIndicator.setContentView(R.layout.dialog_record_voice);
63 | recordIndicator.setCanceledOnTouchOutside(false);
64 | recordIndicator.setCancelable(false);
65 | mVolumeIv = (ImageView) recordIndicator.findViewById(R.id.iv_voice);
66 | voicLine= (VoiceLineView) recordIndicator.findViewById(R.id.voicLine);
67 | mRecordHintTv = (TextView) recordIndicator.findViewById(R.id.tv_length);
68 | mRecordHintTv.setText("00:00:00");
69 | mIvPauseContinue= (ImageView) recordIndicator.findViewById(R.id.iv_continue_or_pause);
70 | mIvComplete= (ImageView) recordIndicator.findViewById(R.id.iv_complete);
71 | recordIndicator.show();
72 | //暂停或继续
73 | mIvPauseContinue.setOnClickListener(new OnClickListener() {
74 | @Override
75 | public void onClick(View view) {
76 | if(voiceManager!=null){
77 | voiceManager.pauseOrStartVoiceRecord();
78 | }
79 | }
80 | });
81 | //完成
82 | mIvComplete.setOnClickListener(new OnClickListener() {
83 | @Override
84 | public void onClick(View view) {
85 | if(voiceManager!=null){
86 | voiceManager.stopVoiceRecord();
87 | }
88 | recordIndicator.dismiss();
89 | }
90 | });
91 | }
92 |
93 | @Override
94 | public void onClick(View view) {
95 | startRecordDialog();
96 | voiceManager.setVoiceRecordListener(new VoiceManager.VoiceRecordCallBack() {
97 | @Override
98 | public void recDoing(long time, String strTime) {
99 | mRecordHintTv.setText(strTime);
100 | }
101 |
102 | @Override
103 | public void recVoiceGrade(int grade) {
104 | voicLine.setVolume(grade);
105 | }
106 |
107 | @Override
108 | public void recStart(boolean init) {
109 | mIvPauseContinue.setImageResource(R.drawable.icon_pause);
110 | voicLine.setContinue();
111 | }
112 |
113 | @Override
114 | public void recPause(String str) {
115 | mIvPauseContinue.setImageResource(R.drawable.icon_continue);
116 | voicLine.setPause();
117 | }
118 |
119 |
120 | @Override
121 | public void recFinish(long length, String strLength, String path) {
122 | if (enRecordVoiceListener != null) {
123 | enRecordVoiceListener.onFinishRecord(length, strLength, path);
124 | }
125 | }
126 | });
127 | voiceManager.startVoiceRecord(Environment.getExternalStorageDirectory().getPath()+"/VoiceManager/audio");
128 | }
129 |
130 | /**
131 | * 结束回调监听
132 | */
133 | public interface EnRecordVoiceListener {
134 | void onFinishRecord(long length, String strLength, String filePath);
135 | }
136 |
137 |
138 | }
139 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/ViewHolderUtil.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 | import android.util.SparseArray;
4 | import android.view.View;
5 |
6 | /**
7 | * 类名:BaseCommonAdapter.java
8 | * 描述:viewholder工具
9 | * 作者:xsf
10 | * 创建时间:2016/11/24
11 | * 最后修改时间:2016/11/24
12 | */
13 | public class ViewHolderUtil {
14 |
15 | @SuppressWarnings("unchecked")
16 | public static T get(View view, int id) {
17 | SparseArray viewHolder = (SparseArray) view.getTag();
18 | if (viewHolder == null) {
19 | viewHolder = new SparseArray();
20 | view.setTag(viewHolder);
21 | }
22 | View childView = viewHolder.get(id);
23 | if (childView == null) {
24 | childView = view.findViewById(id);
25 | viewHolder.put(id, childView);
26 | }
27 | return (T) childView;
28 | }
29 |
30 | /*
31 | * 在getview里面的用法
32 | *
33 | * @Override public View getView(int position, View convertView, ViewGroup
34 | * parent) {
35 | *
36 | * if (convertView == null) { convertView = LayoutInflater.from(context)
37 | * .inflate(R.layout.banana_phone, parent, false); }
38 | *
39 | * ImageView bananaView = ViewHolderUtil.get(convertView, R.id.banana);
40 | * TextView phoneView = ViewHolderUtil.get(convertView, R.id.phone);
41 | *
42 | * BananaPhone bananaPhone = getItem(position);
43 | * phoneView.setText(bananaPhone.getPhone());
44 | * bananaView.setImageResource(bananaPhone.getBanana());
45 | *
46 | * return convertView;
47 | * }
48 | */
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/Voice.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 | /**
4 | * 类名:Voice.java
5 | * 描述:
6 | * 作者:xsf
7 | * 创建时间:2017/3/16
8 | * 最后修改时间:2017/3/16
9 | */
10 |
11 | public class Voice {
12 | private String filePath;
13 | private long length;
14 | private String strLength;
15 |
16 | public Voice(long length,String strLength,String filePath){
17 | this.length=length;
18 | this.strLength=strLength;
19 | this.filePath=filePath;
20 | }
21 |
22 | public String getFilePath() {
23 | return filePath;
24 | }
25 |
26 | public void setFilePath(String filePath) {
27 | this.filePath = filePath;
28 | }
29 |
30 | public long getLength() {
31 | return length;
32 | }
33 |
34 | public void setLength(long length) {
35 | this.length = length;
36 | }
37 |
38 | public String getStrLength() {
39 | return strLength;
40 | }
41 |
42 | public void setStrLength(String strLength) {
43 | this.strLength = strLength;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/VoiceAdapter.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 | import android.content.Context;
4 | import android.graphics.drawable.AnimationDrawable;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.ImageView;
8 | import android.widget.LinearLayout;
9 | import android.widget.TextView;
10 |
11 | /**
12 | * 类名:VoiceAdapter.java
13 | * 描述:
14 | * 作者:xsf
15 | * 创建时间:2017/3/16
16 | * 最后修改时间:2017/3/16
17 | */
18 |
19 | public class VoiceAdapter extends BaseCommonAdapter {
20 | // 语音动画
21 | private AnimationDrawable voiceAnimation;
22 | private VoiceManager voiceManager;
23 | private int lastPosition = -1;
24 |
25 | public VoiceAdapter(Context context) {
26 | super(context);
27 | voiceManager = VoiceManager.getInstance(context);
28 | }
29 |
30 | @Override
31 | public View getView(final int position, View convertView, ViewGroup parent) {
32 | if (convertView == null) {
33 | convertView = mInflater.inflate(R.layout.item_voice_list, parent, false);
34 | }
35 | final ImageView ivVoic = ViewHolderUtil.get(convertView, R.id.iv_voice);
36 | final LinearLayout llRoot = ViewHolderUtil.get(convertView, R.id.ll_root);
37 | TextView tvLength = ViewHolderUtil.get(convertView, R.id.tv_length);
38 | final Voice voice = getData().get(position);
39 | tvLength.setText(voice.getStrLength());
40 | //播放
41 | llRoot.setOnClickListener(new View.OnClickListener() {
42 | @Override
43 | public void onClick(View view) {
44 | if (voiceAnimation != null) {
45 | voiceAnimation.stop();
46 | voiceAnimation.selectDrawable(0);
47 | }
48 | if (voiceManager.isPlaying()&&lastPosition == position) {
49 | voiceManager.stopPlay();
50 | }else{
51 | voiceManager.stopPlay();
52 | voiceAnimation = (AnimationDrawable) ivVoic.getBackground();
53 | voiceAnimation.start();
54 | voiceManager.setVoicePlayListener(new VoiceManager.VoicePlayCallBack() {
55 | @Override
56 | public void voiceTotalLength(long time, String strTime) {
57 |
58 | }
59 |
60 | @Override
61 | public void playDoing(long time, String strTime) {
62 |
63 | }
64 |
65 | @Override
66 | public void playPause() {
67 |
68 | }
69 |
70 | @Override
71 | public void playStart() {
72 |
73 | }
74 |
75 | @Override
76 | public void playFinish() {
77 | if (voiceAnimation != null) {
78 | voiceAnimation.stop();
79 | voiceAnimation.selectDrawable(0);
80 | }
81 | }
82 | });
83 | voiceManager.startPlay(voice.getFilePath());
84 | }
85 | lastPosition = position;
86 |
87 | }
88 | });
89 |
90 | return convertView;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/VoiceLineView.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 | import android.content.Context;
4 | import android.content.res.TypedArray;
5 | import android.graphics.Canvas;
6 | import android.graphics.Color;
7 | import android.graphics.Paint;
8 | import android.graphics.Path;
9 | import android.graphics.Rect;
10 | import android.util.AttributeSet;
11 | import android.view.View;
12 |
13 | import java.util.ArrayList;
14 | import java.util.LinkedList;
15 | import java.util.List;
16 |
17 | /**
18 | * Created by carlos on 2016/1/29.
19 | * 自定义声音振动曲线view
20 | */
21 | public class VoiceLineView extends View {
22 | private final int LINE = 0;
23 | private final int RECT = 1;
24 |
25 | private int middleLineColor = Color.BLACK;
26 | private int voiceLineColor = Color.BLACK;
27 | private float middleLineHeight = 4;
28 | private Paint paint;
29 | private Paint paintVoicLine;
30 | private int mode;
31 | /**
32 | * 灵敏度
33 | */
34 | private int sensibility = 4;
35 |
36 | private float maxVolume = 100;
37 |
38 |
39 | private float translateX = 0;
40 | private boolean isSet = false;
41 |
42 | /**
43 | * 振幅
44 | */
45 | private float amplitude = 1;
46 | /**
47 | * 音量
48 | */
49 | private float volume = 10;
50 | private int fineness = 1;
51 | private float targetVolume = 1;
52 |
53 |
54 | private long speedY = 50;
55 | private float rectWidth = 25;
56 | private float rectSpace = 5;
57 | private float rectInitHeight = 4;
58 | private List rectList;
59 |
60 | private long lastTime = 0;
61 | private int lineSpeed = 90;
62 |
63 | List paths = null;
64 | /**
65 | * 继续或停止震动,默认继续
66 | */
67 | private boolean continueOrPause=true;
68 |
69 | public VoiceLineView(Context context) {
70 | super(context);
71 | }
72 |
73 | public VoiceLineView(Context context, AttributeSet attrs) {
74 | super(context, attrs);
75 | initAtts(context, attrs);
76 | }
77 |
78 | public VoiceLineView(Context context, AttributeSet attrs, int defStyleAttr) {
79 | super(context, attrs, defStyleAttr);
80 | initAtts(context, attrs);
81 | }
82 |
83 | private void initAtts(Context context, AttributeSet attrs) {
84 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.voiceView);
85 | mode = typedArray.getInt(R.styleable.voiceView_viewMode, 0);
86 | voiceLineColor = typedArray.getColor(R.styleable.voiceView_voiceLine, Color.BLACK);
87 | maxVolume = typedArray.getFloat(R.styleable.voiceView_maxVolume, 100);
88 | sensibility = typedArray.getInt(R.styleable.voiceView_sensibility, 4);
89 | if (mode == RECT) {
90 | rectWidth = typedArray.getDimension(R.styleable.voiceView_rectWidth, 25);
91 | rectSpace = typedArray.getDimension(R.styleable.voiceView_rectSpace, 5);
92 | rectInitHeight = typedArray.getDimension(R.styleable.voiceView_rectInitHeight, 4);
93 | } else {
94 | middleLineColor = typedArray.getColor(R.styleable.voiceView_middleLine, Color.BLACK);
95 | middleLineHeight = typedArray.getDimension(R.styleable.voiceView_middleLineHeight, 4);
96 | lineSpeed = typedArray.getInt(R.styleable.voiceView_lineSpeed, 90);
97 | fineness = typedArray.getInt(R.styleable.voiceView_fineness, 1);
98 | paths = new ArrayList<>(20);
99 | for (int i = 0; i < 20; i++) {
100 | paths.add(new Path());
101 | }
102 | }
103 | typedArray.recycle();
104 | }
105 |
106 | @Override
107 | protected void onDraw(Canvas canvas) {
108 | if (mode == RECT) {
109 | drawVoiceRect(canvas);
110 | run();
111 | } else {
112 | drawMiddleLine(canvas);
113 | if(continueOrPause) {
114 | drawVoiceLine(canvas);
115 | run();
116 | }
117 | }
118 | }
119 |
120 | private void drawMiddleLine(Canvas canvas) {
121 | if (paint == null) {
122 | paint = new Paint();
123 | paint.setColor(middleLineColor);
124 | paint.setAntiAlias(true);
125 | }
126 | canvas.save();
127 | canvas.drawRect(0, getHeight() / 2 - middleLineHeight / 2, getWidth(), getHeight() / 2 + middleLineHeight / 2, paint);
128 | canvas.restore();
129 | }
130 |
131 | private void drawVoiceLine(Canvas canvas) {
132 | lineChange();
133 | if (paintVoicLine == null) {
134 | paintVoicLine = new Paint();
135 | paintVoicLine.setColor(voiceLineColor);
136 | paintVoicLine.setAntiAlias(true);
137 | paintVoicLine.setStyle(Paint.Style.STROKE);
138 | paintVoicLine.setStrokeWidth(2);
139 | }
140 | canvas.save();
141 | int moveY = getHeight() / 2;
142 | for (int i = 0; i < paths.size(); i++) {
143 | paths.get(i).reset();
144 | paths.get(i).moveTo(getWidth(), getHeight() / 2);
145 | }
146 | for (float i = getWidth() - 1; i >= 0; i -= fineness) {
147 | amplitude = 4 * volume * i / getWidth() - 4 * volume * i * i / getWidth() / getWidth();
148 | for (int n = 1; n <= paths.size(); n++) {
149 | float sin = amplitude * (float) Math.sin((i - Math.pow(1.22, n)) * Math.PI / 180 - translateX);
150 | paths.get(n - 1).lineTo(i, (2 * n * sin / paths.size() - 15 * sin / paths.size() + moveY));
151 | }
152 | }
153 | for (int n = 0; n < paths.size(); n++) {
154 | if (n == paths.size() - 1) {
155 | paintVoicLine.setAlpha(255);
156 | } else {
157 | paintVoicLine.setAlpha(n * 130 / paths.size());
158 | }
159 | if (paintVoicLine.getAlpha() > 0) {
160 | canvas.drawPath(paths.get(n), paintVoicLine);
161 | }
162 | }
163 | canvas.restore();
164 | }
165 |
166 | private void drawVoiceRect(Canvas canvas) {
167 | if (paintVoicLine == null) {
168 | paintVoicLine = new Paint();
169 | paintVoicLine.setColor(voiceLineColor);
170 | paintVoicLine.setAntiAlias(true);
171 | paintVoicLine.setStyle(Paint.Style.STROKE);
172 | paintVoicLine.setStrokeWidth(2);
173 | }
174 | if (rectList == null) {
175 | rectList = new LinkedList<>();
176 | }
177 | int totalWidth = (int) (rectSpace + rectWidth);
178 | if (speedY % totalWidth < 6) {
179 | Rect rect = new Rect((int) (-rectWidth - 10 - speedY + speedY % totalWidth),
180 | (int) (getHeight() / 2 - rectInitHeight / 2 - (volume == 10 ? 0 : volume / 2)),
181 | (int) (-10 - speedY + speedY % totalWidth),
182 | (int) (getHeight() / 2 + rectInitHeight / 2 + (volume == 10 ? 0 : volume / 2)));
183 | if (rectList.size() > getWidth() / (rectSpace + rectWidth) + 2) {
184 | rectList.remove(0);
185 | }
186 | rectList.add(rect);
187 | }
188 | canvas.translate(speedY, 0);
189 | for (int i = rectList.size() - 1; i >= 0; i--) {
190 | canvas.drawRect(rectList.get(i), paintVoicLine);
191 | }
192 | rectChange();
193 | }
194 |
195 | public void setVolume(int volume) {
196 | if (volume > maxVolume * sensibility / 25) {
197 | isSet = true;
198 | this.targetVolume = getHeight() * volume / 2 / maxVolume;
199 | }
200 | }
201 |
202 | /**
203 | * 停止
204 | */
205 | public void setPause(){
206 | continueOrPause=false;
207 | }
208 | /**
209 | * 继续
210 | */
211 | public void setContinue(){
212 | continueOrPause=true;
213 | run();
214 | }
215 |
216 | private void lineChange() {
217 | if (lastTime == 0) {
218 | lastTime = System.currentTimeMillis();
219 | translateX += 1.5;
220 | } else {
221 | if (System.currentTimeMillis() - lastTime > lineSpeed) {
222 | lastTime = System.currentTimeMillis();
223 | translateX += 1.5;
224 | } else {
225 | return;
226 | }
227 | }
228 | if (volume < targetVolume && isSet) {
229 | volume += getHeight() / 30;
230 | } else {
231 | isSet = false;
232 | if (volume <= 10) {
233 | volume = 10;
234 | } else {
235 | if (volume < getHeight() / 30) {
236 | volume -= getHeight() / 60;
237 | } else {
238 | volume -= getHeight() / 30;
239 | }
240 | }
241 | }
242 | }
243 |
244 | private void rectChange() {
245 | speedY += 6;
246 | if (volume < targetVolume && isSet) {
247 | volume += getHeight() / 30;
248 | } else {
249 | isSet = false;
250 | if (volume <= 10) {
251 | volume = 10;
252 | } else {
253 | if (volume < getHeight() / 30) {
254 | volume -= getHeight() / 60;
255 | } else {
256 | volume -= getHeight() / 30;
257 | }
258 | }
259 | }
260 | }
261 |
262 | public void run() {
263 | if (mode == RECT) {
264 | postInvalidateDelayed(30);
265 | } else {
266 | invalidate();
267 | }
268 | }
269 |
270 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/VoiceManager.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.media.MediaPlayer;
7 | import android.media.MediaRecorder;
8 | import android.os.Environment;
9 | import android.os.Handler;
10 | import android.os.Message;
11 | import android.text.TextUtils;
12 | import android.util.Log;
13 | import android.widget.SeekBar;
14 | import android.widget.Toast;
15 |
16 | import java.io.File;
17 | import java.io.FileInputStream;
18 | import java.io.FileOutputStream;
19 | import java.io.IOException;
20 | import java.util.ArrayList;
21 |
22 | /**
23 | * 录音管理类(包括录音和播放)
24 | * 作者:xsf
25 | * 创建时间:2017/3/16
26 | * 最后修改时间:2017/3/16
27 | */
28 | /**
29 | * 录音管理(包括播放和监听)
30 | */
31 | public class VoiceManager {
32 |
33 | private static volatile VoiceManager voiceManager;
34 | public final int MSG_TIME_INTERVAL = 100;
35 | // 多媒体例如声音的状态
36 | public final int MEDIA_STATE_UNDEFINE = 200;
37 | public final int MEDIA_STATE_RECORD_STOP = 210;
38 | public final int MEDIA_STATE_RECORD_DOING = 220;
39 | public final int MEDIA_STATE_RECORD_PAUSE = 230;
40 | public final int MEDIA_STATE_PLAY_STOP = 310;
41 | public final int MEDIA_STATE_PLAY_DOING = 320;
42 | public final int MEDIA_STATE_PLAY_PAUSE = 330;
43 |
44 | private ArrayList mRecList = new ArrayList();
45 | private Context mContext = null;
46 | private SeekBar mSBPlayProgress;
47 | private int mSavedState, mDeviceState = MEDIA_STATE_UNDEFINE;
48 | private MediaRecorder mMediaRecorder = null;
49 | private MediaPlayer mMediaPlayer = null;
50 | private String mRecTimePrev;
51 | private long mRecTimeSum = 0;
52 | /**
53 | * 录音文件存放的位置(文件夹)
54 | */
55 | private String recordFilePath = "";
56 | /**
57 | * 播放音频文件位置
58 | */
59 | private String playFilePath;
60 | /**
61 | * 录音监听
62 | */
63 | private VoiceRecordCallBack voiceRecordCallBack;
64 | private ObtainDecibelThread mThread;
65 | /**
66 | * 播放监听
67 | */
68 | private VoicePlayCallBack voicePlayCallBack;
69 |
70 | private VoiceManager(Context context) {
71 | this.mContext = context;
72 | }
73 | /**
74 | * 获取单例
75 | * @param context
76 | * @return
77 | */
78 | public static VoiceManager getInstance(Context context){
79 | if(voiceManager == null){
80 | synchronized(VoiceManager.class){
81 | if(voiceManager == null){
82 | voiceManager = new VoiceManager(context);
83 | }
84 | }
85 | }
86 | return voiceManager;
87 | }
88 | /**
89 | * 播放器结束监听
90 | */
91 | private MediaPlayer.OnCompletionListener mPlayCompetedListener = new MediaPlayer.OnCompletionListener() {
92 | @Override
93 | public void onCompletion(MediaPlayer mp) {
94 | mDeviceState = MEDIA_STATE_PLAY_STOP;
95 | mHandler.removeMessages(MSG_TIME_INTERVAL);
96 | mMediaPlayer.stop();
97 | mMediaPlayer.release();
98 | if (mSBPlayProgress != null) {
99 | mSBPlayProgress.setProgress(0);
100 | }
101 | if (voicePlayCallBack != null) {
102 | voicePlayCallBack.playFinish();
103 | }
104 | }
105 | };
106 | /**
107 | * 播放或录音handler
108 | */
109 | @SuppressLint("HandlerLeak")
110 | private final Handler mHandler = new Handler() {
111 | @Override
112 | public void handleMessage(Message msg) {
113 | VoiceTimeUtils ts;
114 | int current;
115 | try {
116 | switch (msg.what) {
117 | //录音
118 | case MSG_TIME_INTERVAL:
119 | if (mDeviceState == MEDIA_STATE_RECORD_DOING) {
120 | ts = VoiceTimeUtils.timeSpanToNow(mRecTimePrev);
121 | mRecTimeSum += ts.mDiffSecond;
122 | mRecTimePrev = VoiceTimeUtils.getTimeStrFromMillis(ts.mNowTime);
123 | ts = VoiceTimeUtils.timeSpanSecond(mRecTimeSum);
124 | //回调录音时间
125 | if (voiceRecordCallBack != null) {
126 | voiceRecordCallBack.recDoing(mRecTimeSum, String.format("%02d:%02d:%02d",
127 | ts.mSpanHour, ts.mSpanMinute, ts.mSpanSecond));
128 | }
129 | mHandler.sendEmptyMessageDelayed(MSG_TIME_INTERVAL, 1000);
130 | }
131 | //播放
132 | else if (mDeviceState == MEDIA_STATE_PLAY_DOING) {
133 | current = mMediaPlayer.getCurrentPosition();
134 | if (mSBPlayProgress != null) {
135 | mSBPlayProgress.setProgress(current);
136 | }
137 | ts = VoiceTimeUtils.timeSpanSecond(current / 1000);
138 | //回调播放进度
139 | if (voicePlayCallBack != null) {
140 | voicePlayCallBack.playDoing(current / 1000, String.format("%02d:%02d:%02d",
141 | ts.mSpanHour, ts.mSpanMinute, ts.mSpanSecond));
142 | }
143 | mHandler.sendEmptyMessageDelayed(MSG_TIME_INTERVAL, 1000);
144 | }
145 | break;
146 | default:
147 | break;
148 | }
149 | } catch (Exception e) {
150 | }
151 | }
152 | };
153 | /*********************************录音操作begin***************************/
154 | /**
155 | * 录音监听
156 | *
157 | * @param callBack
158 | */
159 | public void setVoiceRecordListener(VoiceRecordCallBack callBack) {
160 | voiceRecordCallBack = callBack;
161 | }
162 |
163 | /**
164 | * 开始录音(外部调)
165 | *
166 | * @param filePath 音频存放文件夹
167 | */
168 | public void startVoiceRecord(String filePath) {
169 | if (!isSDCardAvailable()) return ;
170 | this.recordFilePath = filePath;
171 | startVoiceRecord(true);
172 | }
173 |
174 | /**
175 | * 继续或暂停录音
176 | */
177 | public void pauseOrStartVoiceRecord() {
178 | if (mDeviceState == MEDIA_STATE_RECORD_DOING) {
179 | mDeviceState = MEDIA_STATE_RECORD_PAUSE;
180 | stopRecorder(mMediaRecorder, true);
181 | mMediaRecorder = null;
182 | voiceRecordCallBack.recPause("已暂停");
183 | } else {
184 | startVoiceRecord(false);
185 | }
186 | }
187 |
188 | /**
189 | * 完成录音
190 | */
191 | public void stopVoiceRecord() {
192 | try {
193 | mHandler.removeMessages(MSG_TIME_INTERVAL);
194 | mDeviceState = MEDIA_STATE_RECORD_STOP;
195 | stopRecorder(mMediaRecorder, true);
196 | mMediaRecorder = null;
197 | if (VoiceTimeUtils.timeSpanSecond(mRecTimeSum).mSpanSecond == 0) {
198 | Toast.makeText(mContext, "时间过短", Toast.LENGTH_SHORT).show();
199 | } else {
200 | File file = getOutputVoiceFile(mRecList);
201 | if (file != null && file.length() > 0) {
202 | cleanFieArrayList(mRecList);
203 | //TODO 这里可以返回数据 setResult
204 | final VoiceTimeUtils ts = VoiceTimeUtils.timeSpanSecond(mRecTimeSum);
205 | //完成录音
206 | if (voiceRecordCallBack != null) {
207 | voiceRecordCallBack.recFinish(mRecTimeSum, String.format("%02d:%02d:%02d",
208 | ts.mSpanHour, ts.mSpanMinute, ts.mSpanSecond),file.getAbsolutePath());
209 | }
210 | }
211 | }
212 |
213 | } catch (Exception e) {
214 | e.printStackTrace();
215 | }
216 | }
217 |
218 | /**
219 | * 开始录音(内部调)
220 | *
221 | * @param init 是否是初始化录音还是暂停后再录音
222 | */
223 | private void startVoiceRecord(boolean init) {
224 | if (!isSDCardAvailable()) return;
225 | if (init) {
226 | mRecTimeSum = 0;
227 | cleanFieArrayList(mRecList);
228 | }
229 | //录音前停止播放回调
230 | if(voicePlayCallBack!=null){
231 | voicePlayCallBack.playFinish();
232 | }
233 | stopRecorder(mMediaRecorder, true);
234 | mMediaRecorder = null;
235 |
236 | stopMedia(mMediaPlayer, true);
237 | mMediaPlayer = null;
238 | mMediaRecorder = new MediaRecorder();
239 | File file = prepareRecorder(mMediaRecorder, true);
240 | if (file != null) {
241 | //开始录音回调
242 | if (voiceRecordCallBack != null) {
243 | voiceRecordCallBack.recStart(init);
244 | }
245 | mDeviceState = MEDIA_STATE_RECORD_DOING;
246 | mRecTimePrev = VoiceTimeUtils.getTimeStrFromMillis(System.currentTimeMillis());
247 | mRecList.add(file);
248 |
249 | mHandler.removeMessages(MSG_TIME_INTERVAL);
250 | mHandler.sendEmptyMessage(MSG_TIME_INTERVAL);
251 | }
252 | }
253 |
254 | /**
255 | * 监听录音声音频率大小
256 | */
257 | private class ObtainDecibelThread extends Thread {
258 |
259 | private volatile boolean running = true;
260 |
261 | public void exit() {
262 | running = false;
263 | }
264 | @Override
265 | public void run() {
266 | while (running) {
267 | try {
268 | Thread.sleep(100);
269 | } catch (InterruptedException e) {
270 | e.printStackTrace();
271 | }
272 | if (mMediaRecorder == null || !running) {
273 | break;
274 | }
275 | try {
276 | final double ratio = mMediaRecorder.getMaxAmplitude()/150;
277 | if (ratio != 0&&voiceRecordCallBack!=null) {
278 | ((Activity)mContext).runOnUiThread(new Runnable() {
279 | @Override
280 | public void run() {
281 | double db=0;// 分贝
282 | if (ratio > 1)
283 | db = (int) (20 * Math.log10(ratio));
284 | voiceRecordCallBack.recVoiceGrade((int)db);
285 | }
286 | });
287 | }
288 | } catch (RuntimeException e) {
289 | e.printStackTrace();
290 | }
291 |
292 | }
293 | }
294 |
295 | }
296 |
297 | /**
298 | * 合并录音
299 | *
300 | * @param list
301 | * @return
302 | */
303 | private File getOutputVoiceFile(ArrayList list) {
304 | String mMinute1 = VoiceTimeUtils.getTime();
305 | File recDirFile = recAudioDir(recordFilePath);
306 |
307 | // 创建音频文件,合并的文件放这里
308 | File resFile = new File(recDirFile, mMinute1 + ".amr");
309 | FileOutputStream fileOutputStream = null;
310 | try {
311 | fileOutputStream = new FileOutputStream(resFile);
312 | } catch (IOException e) {
313 | }
314 | // list里面为暂停录音 所产生的 几段录音文件的名字,中间几段文件的减去前面的6个字节头文件
315 | for (int i = 0; i < list.size(); i++) {
316 | File file = list.get(i);
317 | try {
318 | FileInputStream fileInputStream = new FileInputStream(file);
319 | byte[] myByte = new byte[fileInputStream.available()];
320 | // 文件长度
321 | int length = myByte.length;
322 | // 头文件
323 | if (i == 0) {
324 | while (fileInputStream.read(myByte) != -1) {
325 | fileOutputStream.write(myByte, 0, length);
326 | }
327 | }
328 | // 之后的文件,去掉头文件就可以了
329 | else {
330 | while (fileInputStream.read(myByte) != -1) {
331 | fileOutputStream.write(myByte, 6, length - 6);
332 | }
333 | }
334 | fileOutputStream.flush();
335 | fileInputStream.close();
336 | } catch (Exception e) {
337 | e.printStackTrace();
338 | }
339 | }
340 | // 结束后关闭流
341 | try {
342 | fileOutputStream.close();
343 | } catch (IOException e) {
344 | e.printStackTrace();
345 | }
346 |
347 | return resFile;
348 | }
349 |
350 | /**
351 | * 清空暂停录音所产生的几段录音文件
352 | *
353 | * @param list
354 | */
355 | private void cleanFieArrayList(ArrayList list) {
356 | for (File file : list) {
357 | file.delete();
358 | }
359 | list.clear();
360 | }
361 |
362 | /*********************************录音操作end***************************/
363 |
364 |
365 | /*********************************播放操作end***************************/
366 | /**
367 | * 播放监听
368 | *
369 | * @param callBack
370 | */
371 | public void setVoicePlayListener(VoicePlayCallBack callBack) {
372 | voicePlayCallBack = callBack;
373 | }
374 |
375 | /**
376 | * 播放SeekBar监听
377 | *
378 | * @param seekBar
379 | */
380 | public void setSeekBarListener(SeekBar seekBar) {
381 | mSBPlayProgress = seekBar;
382 | if (mSBPlayProgress != null) {
383 | mSBPlayProgress.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
384 | @Override
385 | public void onStartTrackingTouch(SeekBar seekBar) {
386 | mHandler.removeMessages(MSG_TIME_INTERVAL);
387 | mSavedState = mDeviceState;
388 | if (mSavedState == MEDIA_STATE_PLAY_DOING) {
389 | pauseMedia(mMediaPlayer);
390 | }
391 | }
392 |
393 | @Override
394 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
395 | mHandler.removeMessages(MSG_TIME_INTERVAL);
396 | VoiceTimeUtils ts = VoiceTimeUtils.timeSpanSecond(progress / 1000);
397 | //播放进度
398 | if (voicePlayCallBack != null) {
399 | voicePlayCallBack.playDoing(progress / 1000, String.format("%02d:%02d:%02d",
400 | ts.mSpanHour, ts.mSpanMinute, ts.mSpanSecond));
401 | }
402 | }
403 |
404 | @Override
405 | public void onStopTrackingTouch(SeekBar seekBar) {
406 | seektoMedia(mMediaPlayer, mSBPlayProgress.getProgress());
407 |
408 | if (mSavedState == MEDIA_STATE_PLAY_DOING) {
409 | playMedia(mMediaPlayer);
410 | mHandler.sendEmptyMessage(MSG_TIME_INTERVAL);
411 | }
412 | }
413 | });
414 | }
415 | }
416 |
417 | /**
418 | * 开始播放(外部调)
419 | *
420 | * @param filePath 音频存放文件夹
421 | */
422 | public void startPlay(String filePath) {
423 | if (TextUtils.isEmpty(filePath)|| !new File(filePath).exists())
424 | {
425 | if (voicePlayCallBack != null) {
426 | voicePlayCallBack.playFinish();
427 | }
428 | Toast.makeText(mContext,"文件不存在",Toast.LENGTH_SHORT).show();
429 | return;
430 | }else {
431 | playFilePath = filePath;
432 | startPlay(true);
433 | }
434 | }
435 | /**
436 | * 开始播放(内部调)
437 | *
438 | * @param init
439 | */
440 | private void startPlay(boolean init) {
441 | try {
442 | stopRecorder(mMediaRecorder, true);
443 | mMediaRecorder = null;
444 |
445 | stopMedia(mMediaPlayer, true);
446 | mMediaPlayer = null;
447 |
448 | mMediaPlayer = new MediaPlayer();
449 | mMediaPlayer.setOnCompletionListener(mPlayCompetedListener);
450 |
451 | if (prepareMedia(mMediaPlayer, playFilePath)) {
452 | mDeviceState = MEDIA_STATE_PLAY_DOING;
453 | //总时间长度
454 | long totalTime = mMediaPlayer.getDuration() / 1000;
455 | VoiceTimeUtils ts = VoiceTimeUtils.timeSpanSecond(totalTime);
456 | String voiceLength = String.format("%02d:%02d:%02d",
457 | ts.mSpanHour, ts.mSpanMinute, ts.mSpanSecond);
458 | //播放进度回调
459 | if (voicePlayCallBack != null) {
460 | voicePlayCallBack.voiceTotalLength(totalTime, voiceLength);
461 | voicePlayCallBack.playDoing(0, "00:00:00");
462 | }
463 | if (mSBPlayProgress != null) {
464 | mSBPlayProgress.setMax(Math.max(1, mMediaPlayer.getDuration()));
465 | }
466 | if (init) {
467 | if (mSBPlayProgress != null) {
468 | mSBPlayProgress.setProgress(0);
469 | }
470 | seektoMedia(mMediaPlayer, 0);
471 | } else {
472 | seektoMedia(mMediaPlayer, mSBPlayProgress.getProgress());
473 | }
474 | if (playMedia(mMediaPlayer)) {
475 | mHandler.removeMessages(MSG_TIME_INTERVAL);
476 | mHandler.sendEmptyMessage(MSG_TIME_INTERVAL);
477 | }
478 | }
479 | } catch (Exception e) {
480 | Log.e("播放出错了", e.getMessage());
481 | }
482 | }
483 |
484 | /**
485 | * 继续或暂停播放
486 | */
487 | public void continueOrPausePlay() {
488 | if (mDeviceState == MEDIA_STATE_PLAY_DOING) {
489 | mDeviceState = MEDIA_STATE_PLAY_PAUSE;
490 | pauseMedia(mMediaPlayer);
491 | //暂停
492 | if (voicePlayCallBack != null) {
493 | voicePlayCallBack.playPause();
494 | }
495 | } else if (mDeviceState == MEDIA_STATE_PLAY_PAUSE) {
496 | mDeviceState = MEDIA_STATE_PLAY_DOING;
497 | playMedia(mMediaPlayer);
498 | //播放中
499 | mHandler.removeMessages(MSG_TIME_INTERVAL);
500 | mHandler.sendEmptyMessage(MSG_TIME_INTERVAL);
501 | } else if (mDeviceState == MEDIA_STATE_PLAY_STOP) {
502 | //播放
503 | if (!TextUtils.isEmpty(playFilePath)) {
504 | startPlay(false);
505 | }
506 | }
507 | }
508 |
509 | /**
510 | * 停止播放
511 | */
512 | public void stopPlay() {
513 | mHandler.removeMessages(MSG_TIME_INTERVAL);
514 | mDeviceState = MEDIA_STATE_PLAY_STOP;
515 | stopMedia(mMediaPlayer, true);
516 | mMediaPlayer = null;
517 | }
518 |
519 | /**
520 | * 是否在播放中
521 | * @return
522 | */
523 | public boolean isPlaying(){
524 | return mDeviceState == MEDIA_STATE_PLAY_DOING;
525 | }
526 |
527 | /*********************************播放操作end***************************/
528 |
529 |
530 | /**
531 | * 播放录音准备工作
532 | *
533 | * @param mp
534 | * @param file
535 | * @return
536 | */
537 | private boolean prepareMedia(MediaPlayer mp, String file) {
538 | boolean result = false;
539 | try {
540 | mp.setDataSource(file);
541 | mp.prepare();
542 | result = true;
543 | } catch (Exception e) {
544 | }
545 |
546 | return result;
547 | }
548 |
549 | /**
550 | * 播放录音开始
551 | *
552 | * @param mp
553 | * @return
554 | */
555 | private boolean playMedia(MediaPlayer mp) {
556 | boolean result = false;
557 | try {
558 | if (mp != null) {
559 | mp.start();
560 | result = true;
561 | }
562 | } catch (Exception e) {
563 | }
564 |
565 | return result;
566 | }
567 |
568 | /**
569 | * 拖动播放进度条
570 | *
571 | * @param mp
572 | * @param pos
573 | * @return
574 | */
575 | private boolean seektoMedia(MediaPlayer mp, int pos) {
576 | boolean result = false;
577 | try {
578 | if (mp != null && pos >= 0) {
579 | mp.seekTo(pos);
580 | result = true;
581 | }
582 | } catch (Exception e) {
583 | }
584 | return result;
585 | }
586 |
587 | /**
588 | * 停止播放
589 | *
590 | * @param mp
591 | * @param release
592 | * @return
593 | */
594 | private boolean stopMedia(MediaPlayer mp, boolean release) {
595 | boolean result = false;
596 | try {
597 | if (mp != null) {
598 | mp.stop();
599 |
600 | if (release) {
601 | mp.release();
602 |
603 |
604 | }
605 | result = true;
606 | }
607 | } catch (Exception e) {
608 | }
609 |
610 | return result;
611 | }
612 |
613 | /**
614 | * 暂停播放
615 | *
616 | * @param mp
617 | * @return
618 | */
619 | private boolean pauseMedia(MediaPlayer mp) {
620 | boolean result = false;
621 |
622 | try {
623 | if (mp != null) {
624 | mp.pause();
625 | result = true;
626 | }
627 | } catch (Exception e) {
628 | }
629 |
630 | return result;
631 | }
632 |
633 | /**
634 | * 最后停止录音
635 | *
636 | * @param mr
637 | * @param release
638 | * @return
639 | */
640 | private boolean stopRecorder(MediaRecorder mr, boolean release) {
641 | boolean result = false;
642 | try {
643 | if (mr != null) {
644 | mr.stop();
645 | if (release) {
646 | mr.release();
647 | }
648 | result = true;
649 | }
650 | if(mThread!=null){
651 | mThread.exit();
652 | mThread=null;
653 | }
654 | } catch (Exception e) {
655 | if(mThread!=null){
656 | mThread.exit();
657 | mThread=null;
658 | }
659 | }
660 | return result;
661 | }
662 |
663 | /**
664 | * 录音准备工作 ,开始录音
665 | *
666 | * @param mr
667 | * @param start
668 | * @return
669 | */
670 | @SuppressWarnings("deprecation")
671 | private File prepareRecorder(MediaRecorder mr, boolean start) {
672 | File recFile = null;
673 | if (mr == null) return null;
674 | try {
675 | String path = recAudioDir(recordFilePath).getAbsolutePath();
676 | recFile = new File(path, VoiceTimeUtils.getTime() + ".amr");
677 | mr.setAudioSource(MediaRecorder.AudioSource.MIC);
678 | mr.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
679 | mr.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
680 | mr.setOutputFile(recFile.getAbsolutePath());
681 | mr.prepare();
682 | if (start) {
683 | mr.start();
684 | if (mThread == null) {
685 | mThread = new ObtainDecibelThread();
686 | mThread.start();
687 | }
688 | }
689 | } catch (Exception e) {
690 | e.printStackTrace();
691 | }
692 | return recFile;
693 | }
694 |
695 | /**
696 | * 停止录音和播放
697 | */
698 | public void stopRecordAndPlay(){
699 | stopRecorder(mMediaRecorder, true);
700 | mMediaRecorder = null;
701 | stopMedia(mMediaPlayer, true);
702 | mMediaPlayer = null;
703 | }
704 |
705 | /**
706 | * 录音回调监听
707 | */
708 | public interface VoiceRecordCallBack {
709 | //录音中
710 | void recDoing(long time, String strTime);
711 | //录音中的声音频率等级
712 | void recVoiceGrade(int grade);
713 | //录音开始
714 | void recStart(boolean init);
715 |
716 | //录音暂停
717 | void recPause(String str);
718 |
719 | //录音结束
720 | void recFinish(long length, String strLength, String path);
721 | }
722 |
723 | /**
724 | * 播放录音回调监听
725 | */
726 | public interface VoicePlayCallBack {
727 |
728 | /**
729 | * 音频长度
730 | * 指定的某个时间段,以秒为单位
731 | */
732 | void voiceTotalLength(long time, String strTime);
733 |
734 | /**
735 | * 播放中
736 | * 指定的某个时间段,以秒为单位
737 | */
738 | void playDoing(long time, String strTime);
739 |
740 | //播放暂停
741 | void playPause();
742 |
743 | //播放开始
744 | void playStart();
745 |
746 | //播放结束
747 | void playFinish();
748 | }
749 |
750 | /**
751 | * SD卡是否可用
752 | */
753 | public static boolean isSDCardAvailable() {
754 | return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
755 | }
756 |
757 | public static File recAudioDir(String path) {
758 | File file = new File(path);
759 | if (!file.exists()) {
760 | file.mkdirs();
761 | }
762 | return file;
763 | }
764 | }
765 |
--------------------------------------------------------------------------------
/app/src/main/java/com/jaydenxiao/voicemanager/VoiceTimeUtils.java:
--------------------------------------------------------------------------------
1 | package com.jaydenxiao.voicemanager;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.text.TextUtils;
5 |
6 | import java.text.ParseException;
7 | import java.text.SimpleDateFormat;
8 | import java.util.Date;
9 | import java.util.Locale;
10 |
11 | public class VoiceTimeUtils {
12 | /**
13 | * 指定的某个时间,以毫秒为单位
14 | */
15 | public long mOldTime = 0;
16 |
17 | /**
18 | * 当前时间,以毫秒为单位
19 | */
20 | public long mNowTime = 0;
21 |
22 | /**
23 | * 指定的某个时间和当前时间之间的时间间隔,以秒为单位
24 | */
25 | public long mDiffSecond = 0;
26 |
27 | /**
28 | * 时间间隔的天数
29 | */
30 | public long mSpanDay = 0;
31 |
32 | /**
33 | * 时间间隔的小时数
34 | */
35 | public long mSpanHour = 0;
36 |
37 | /**
38 | * 时间间隔的分钟数
39 | */
40 | public long mSpanMinute = 0;
41 |
42 | /**
43 | * 时间间隔的秒数
44 | */
45 | public long mSpanSecond = 0;
46 |
47 | /**
48 | * 时间间隔的天数,以多少天的文字形式输出
49 | */
50 | public String mSpanDayText = "";
51 |
52 | /**
53 | * 时间间隔的小时数,以多少小时的文字形式输出
54 | */
55 | public String mSpanHourText = "";
56 |
57 | /**
58 | * 时间间隔的分钟数,以多少分钟的文字形式输出
59 | */
60 | public String mSpanMinuteText = "";
61 |
62 | /**
63 | * 构造函数
64 | */
65 | private VoiceTimeUtils() {
66 | }
67 |
68 | /**
69 | * 某个时间段长度
70 | *
71 | * @param second 指定的某个时间段,以秒为单位
72 | */
73 | public static VoiceTimeUtils timeSpanSecond(long second) {
74 | VoiceTimeUtils ts = new VoiceTimeUtils();
75 | long remain;
76 |
77 | try {
78 | ts.mDiffSecond = second; // 秒
79 |
80 | // 过期天数
81 | ts.mSpanDay = ts.mDiffSecond / (24 * 3600);
82 | remain = ts.mDiffSecond % (24 * 3600);
83 |
84 | // 过期小时数
85 | if (remain > 0) {
86 | ts.mSpanHour = remain / 3600;
87 | remain = remain % 3600;
88 | }
89 |
90 | // 过期分钟数
91 | if (remain > 0) {
92 | ts.mSpanMinute = remain / 60;
93 | }
94 |
95 | // 过期秒数
96 | ts.mSpanSecond = remain % 60;
97 |
98 | if (ts.mSpanDay > 0) {
99 | ts.mSpanDayText = ts.mSpanDay + "天";
100 | }
101 |
102 | if (ts.mSpanHour > 0) {
103 | ts.mSpanHourText = ts.mSpanHour + "小时";
104 | }
105 |
106 | if (ts.mSpanMinute > 0) {
107 | ts.mSpanMinuteText = ts.mSpanMinute + "分钟";
108 | }
109 | } catch (Exception e) {
110 | }
111 |
112 | return ts;
113 | }
114 |
115 | /**
116 | * 指定某个时间到当前时间的时间段
117 | *
118 | * @param time 指定的某个时间,格式为yyyy-MM-dd HH:mm:ss
119 | */
120 | public static VoiceTimeUtils timeSpanToNow(String time) {
121 | VoiceTimeUtils ts = new VoiceTimeUtils();
122 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
123 | long remain;
124 |
125 | try {
126 | if (TextUtils.isEmpty(time))
127 | return ts;
128 |
129 | ts.mOldTime = sdf.parse(time).getTime();
130 | ts.mNowTime = System.currentTimeMillis();
131 | ts.mDiffSecond = Math.abs((ts.mNowTime - ts.mOldTime) / 1000); // 秒
132 |
133 | // 过期天数
134 | ts.mSpanDay = ts.mDiffSecond / (24 * 3600);
135 | remain = ts.mDiffSecond % (24 * 3600);
136 |
137 | // 过期小时数
138 | if (remain > 0) {
139 | ts.mSpanHour = remain / 3600;
140 | remain = remain % 3600;
141 | }
142 |
143 | // 过期分钟数
144 | if (remain > 0) {
145 | ts.mSpanMinute = remain / 60;
146 | }
147 |
148 | // 过期秒数
149 | ts.mSpanSecond = remain % 60;
150 |
151 | if (ts.mSpanDay > 0) {
152 | ts.mSpanDayText = ts.mSpanDay + "天";
153 | }
154 |
155 | if (ts.mSpanHour > 0) {
156 | ts.mSpanHourText = ts.mSpanHour + "小时";
157 | }
158 |
159 | if (ts.mSpanMinute > 0) {
160 | ts.mSpanMinuteText = ts.mSpanMinute + "分钟";
161 | }
162 | } catch (ParseException e1) {
163 | } catch (Exception e2) {
164 | }
165 |
166 | return ts;
167 | }
168 |
169 | /**
170 | * 将以毫秒为单位的时间转换成字符串格式的时间
171 | *
172 | * @param time 时间,以毫秒为单位
173 | */
174 | public static String getTimeStrFromMillis(long time) {
175 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
176 | String result = sdf.format(new Date(time));
177 | return result;
178 | }
179 |
180 | /**
181 | * 当前时间格式
182 | */
183 | @SuppressLint("SimpleDateFormat")
184 | public static String getTime() {
185 | SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd-HHmmss");
186 | Date curDate = new Date(System.currentTimeMillis());
187 | String time = formatter.format(curDate);
188 | return time;
189 | }
190 | }
191 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/icon_complete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/drawable-hdpi/icon_complete.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/icon_continue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/drawable-hdpi/icon_continue.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/icon_pause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/drawable-hdpi/icon_pause.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/icon_record.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/drawable-hdpi/icon_record.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/icon_voice_record.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/drawable-hdpi/icon_voice_record.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/voicetip_play_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/drawable-hdpi/voicetip_play_1.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/voicetip_play_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/drawable-hdpi/voicetip_play_2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/voicetip_play_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/drawable-hdpi/voicetip_play_3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/playbar_style.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
6 |
7 |
8 |
9 |
10 |
11 | -
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/round_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/round_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
8 |
9 |
10 | -
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/voice_receive.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
7 |
10 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
13 |
14 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_record_voice.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
30 |
31 |
38 |
39 |
50 |
51 |
59 |
60 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_voice_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
15 |
16 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #00000000
4 | #FFFFFF
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | VoiceManager
3 |
4 | Hello world!
5 | Settings
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
15 |
16 |
--------------------------------------------------------------------------------
/art/Screenshot_2017-03-21-17-28-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/art/Screenshot_2017-03-21-17-28-01.png
--------------------------------------------------------------------------------
/art/Screenshot_2017-03-21-17-28-05.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/art/Screenshot_2017-03-21-17-28-05.png
--------------------------------------------------------------------------------
/art/Screenshot_2017-03-21-17-28-26.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/art/Screenshot_2017-03-21-17-28-26.png
--------------------------------------------------------------------------------
/art/art.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/art/art.mp4
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.2.3'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/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 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 10 15:27:10 PDT 2013
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-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/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 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/hisign.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
35 |
36 |
37 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/video_20180702_130810.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jaydenxiao2016/VoiceManager/ed8d2838f5d2f3853f3fc1ced695ed13f9a51675/video_20180702_130810.mp4
--------------------------------------------------------------------------------