├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── libraries │ ├── animated_vector_drawable_23_4_0.xml │ ├── appcompat_v7_23_4_0.xml │ ├── hamcrest_core_1_3.xml │ ├── junit_4_12.xml │ ├── rxandroidaudio_1_2_2.xml │ ├── rxjava_1_1_0.xml │ ├── support_annotations_23_4_0.xml │ ├── support_v4_23_4_0.xml │ └── support_vector_drawable_23_4_0.xml ├── misc.xml ├── modules.xml └── runConfigurations.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── me │ │ └── jessyan │ │ └── audiomanager │ │ └── demo │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── me │ │ │ └── jessyan │ │ │ └── audiomanager │ │ │ └── demo │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── me │ └── jessyan │ └── audiomanager │ └── demo │ └── ExampleUnitTest.java ├── audiomanagersdk ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── me │ │ └── jessyan │ │ └── audiomanager │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── me │ │ │ └── jessyan │ │ │ └── audiomanager │ │ │ ├── Audio.java │ │ │ ├── AudioListener.java │ │ │ ├── AudioManager.java │ │ │ ├── Post.java │ │ │ ├── StatusListener.java │ │ │ ├── entity │ │ │ └── AudioInfo.java │ │ │ ├── io │ │ │ ├── Executor.java │ │ │ ├── HttpExecutor.java │ │ │ ├── HttpHelper.java │ │ │ ├── onDownloadListener.java │ │ │ └── onNetWorkListener.java │ │ │ └── util │ │ │ ├── DataHelper.java │ │ │ ├── DeviceUtils.java │ │ │ ├── LogUtils.java │ │ │ └── UiUitls.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── me │ └── jessyan │ └── audiomanager │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 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 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | 39 | # Keystore files 40 | *.jks 41 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | AudioManagerSDK -------------------------------------------------------------------------------- /.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/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/libraries/animated_vector_drawable_23_4_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/appcompat_v7_23_4_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /.idea/libraries/hamcrest_core_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/junit_4_12.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/rxandroidaudio_1_2_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/rxjava_1_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/support_annotations_23_4_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/support_v4_23_4_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/libraries/support_vector_drawable_23_4_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AudioManager SDK 2 | ## Introduction 3 | `AudioManager`专注于录音相关逻辑的处理,目前举手发言场景和点名发言场景已经使用`AudioManager`,后续有其他场景要使用到录音相关功能都应使用此类 4 | ## Dev tips 5 | * `AudioManager`独立于举手发言场景,它不与举手发言场景耦合,唯一与举手场景耦合的地方时录音结束后发送消息给主播,每个场景发送的逻辑不一样,所以采用接口的方式(post interface),让调用者实现,此场景下由`RaiseManager`实现. 6 | 7 | * 因为录音相关逻辑涉及到上传和下载的功能,`AudioManager`专注于录音处理,由于网络框架每个项目都有自己独有的一套,且庞大,所以调用者通过`HttpHelper`接口,自己实现上传下载的功能,这样保证了扩展又保证了**SDK**的大小. 8 | * `AudioInfo`表示录音的消息,里面封装有基本的信息,为了扩展,`AudioManager`可以传入一个继承于`AudioInfo`的范型,这样就可以在回调中收到自己定义的录音消息对象,可以储存一些自己定义的信息. 9 | 10 | ## 参数及方法介绍: 11 | ### 构建`AudioManager `需要传入的参数 12 | * 必要参数 13 | * **Context**: 上下文 14 | * **HttpHelper**: 用于上传下载录音的接口,每个应用场景必须自己实现 15 | * 非必要参数 16 | * **MaxRecordTime**: 单次录音最大录制时间,超时会在AudioListener中回调 17 | * **MinRecordTime**: 单次录音最小录音时间,超时会在AudioListener中回调 18 | * **AudioListener**: 录音时的一些信息回调(与MaxRecordTime和MinRecordTime有关) 19 | * **StatusListener**: 对于录音信息的一些状态回调(录音上传中,播放中,下载失败...) 20 | 21 | ### 主要方法 22 | * **startRecord**: 开始录制语音 23 | * **stopRecord**: 停止录制语音,将当前录制的语音自动加入到队列中 24 | * **cancel**: 取消录制 25 | * **startUpload**: 开始上传录音,指定上传某个录音,也可以自动取队列的首个录音上传,如果上传失败他会在后台自动续传 26 | * **download**: 下载指定的录音 27 | * **addSpeech**: `AudioManager `自己管理一个用于展示到**UI**的**List**,此方法为添加到此列表 28 | * **getSpeechList**: 获得用于展示到**UI**的**List**,比如给**RecycleView**使用 29 | * **play**: 播放录音,内部有一个队列,如果在一个录音播放完之前被调用多次,则后面加入的录音都在队列中,等待播放,播放完上一个录音会检测队列,有等待的录音则继续播放下一个 30 | * **palySingle**: 播放录音,内部没有队列,调用时如果之前的录音还没播放完则停止之前的录音,立即开始现在录音的播放 31 | * **stopPlay**: 停止播放所有录音 32 | * **release**: 释放资源 33 | 34 | ### 示例代码 35 | ``` java 36 | mAudioManager = AudioManager 37 | .builder() 38 | .httpHelper(new OkHttpHelper(getActivity())) 39 | .minRecordTime(2, TimeUnit.SECONDS) 40 | .with(getActivity()) 41 | .addAudioListener(AudioListener) 42 | .addStatusListener(StatusListener) 43 | .build(); 44 | ``` 45 | 46 | ## 详解 47 | ### AudioInfo structure 48 | 49 | Field | Description 50 | :----------:|:-------------: 51 | filePath | 录音文件本地路径 52 | url | 上传到服务器的url地址 53 | recordTime | 录制的时间单位秒 54 | ctime | 创建的时间单位毫秒,作为此录音的唯一码 55 | status | 0.正常状态 1.下载中 2.播放失败 3.播放中 4.播放暂停 56 | 57 | ### AudioListener Introduction 58 | Method | Description 59 | :-------------------:|:-------------: 60 | onRecordTooShort | 单次录制时间少于MinRecordTime,则调用此回调 61 | onRecordTooLong | 单次录制时间大于MaxRecordTime,则调用此回调 62 | onVoiceAmplitudeLevel| 在录制时会执行此回调,会提供声音的大小,可以做一些**UI**操作 63 | 64 | ### StatusListener Introduction 65 | 录音状态信息有变化则会调用此回调(如录音下载成功,失败...),回调提供一个继承于`AudioInfo`的范型(可自己指定,便于存放一些自定义消息)以及一个状态码,告诉开发者当前录音的状态 66 | 67 | StatusCode | Description 68 | :---------:|:--------------: 69 | 0 | 此录音下载中 70 | 1 | 此录音下载失败 71 | 2 | 此录音下载成功 72 | 3 | 此录音播放中 73 | 4 | 此录音播放失败 74 | 5 | 此录音播放完成 75 | 6 | 此录音被加入到列表中 76 | 7 | 此录音在播放中被暂停 77 | 78 | ### Post Interface 79 | 所有应用场景必须实现它,上传成功后便会调用它发送im消息,每个场景自定义自己的逻辑 80 | 81 | ### Executor Interface 82 | 此接口是处理自动续传逻辑,即上传失败后,后台保持队列继续上传直到成功,默认实现为HttpExecutor,可以实现`Executor Interface`自定义自己的自动续传逻辑. 83 | 84 | ### HttpHelper Interface 85 | 此接口是上传下载录音的接口,没有默认实现,如果要想上传下载录音必须,自己实现,原因上面已经提及 86 | 87 | ### Audio Interface 88 | `AudioManager`就实现于此接口,如果你觉得此录音管理类写的太渣了,你完全可以实现此接口重新写相关的逻辑,完全不用改之前的所有代码,此**SDK**大量使用接口处理扩展和低耦合,后面的场景应该是用此开发模式 89 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | applicationId "me.jessyan.audiomanager.demo" 9 | minSdkVersion 15 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(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:appcompat-v7:23.4.0' 26 | compile project(':audiomanagersdk') 27 | } 28 | -------------------------------------------------------------------------------- /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/jess/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/me/jessyan/audiomanager/demo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.demo; 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 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/me/jessyan/audiomanager/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.demo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import me.jessyan.audiomanager.AudioListener; 9 | import me.jessyan.audiomanager.AudioManager; 10 | import me.jessyan.audiomanager.StatusListener; 11 | import me.jessyan.audiomanager.entity.AudioInfo; 12 | import me.jessyan.audiomanager.io.HttpHelper; 13 | import me.jessyan.audiomanager.io.onDownloadListener; 14 | import me.jessyan.audiomanager.io.onNetWorkListener; 15 | 16 | public class MainActivity extends AppCompatActivity { 17 | 18 | private AudioManager mAudioManager; 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_main); 24 | initAudioManager(); 25 | 26 | } 27 | 28 | private void initAudioManager() { 29 | mAudioManager = AudioManager 30 | .builder() 31 | .with(this) 32 | .minRecordTime(2, TimeUnit.SECONDS) 33 | .httpHelper(new HttpHelper() {//外部实现上传下载功能 34 | @Override 35 | public void upload(String filePath, onNetWorkListener listener) {//上传 36 | 37 | } 38 | 39 | @Override 40 | public void download(String url, onDownloadListener listener) {//下载 41 | 42 | } 43 | 44 | @Override 45 | public void release() {//释放一些资源 46 | 47 | } 48 | }).addAudioListener(new AudioListener() { 49 | @Override 50 | public void onRecordTooShort() {//当前录音时间太短 51 | 52 | } 53 | 54 | @Override 55 | public void onRecordTooLong() {//当前录音是时间太长 56 | 57 | } 58 | 59 | @Override 60 | public void onVoiceAmplitudeLevel(int level) {//录音音量回调 61 | 62 | } 63 | }).addStatusListener(new StatusListener() { 64 | @Override 65 | public void onStatusChange(AudioInfo info, int status) { 66 | switch (status) { 67 | case AudioManager.STATUS_ADD_SPEECH_LIST://添加到列表 68 | break; 69 | case AudioManager.STATUS_DOWNLOAD_FAILURE://下载失败 70 | break; 71 | case AudioManager.STATUS_DOWNLOAD_SUCCESS://下载成功 72 | break; 73 | case AudioManager.STATUS_DOWNLOADING://下载中 74 | break; 75 | case AudioManager.STATUS_PLAY_COMPLETE://播放完成 76 | break; 77 | case AudioManager.STATUS_PLAY_FAILURE://播放失败 78 | break; 79 | case AudioManager.STATUS_PLAY_PAUSE://播放停止 80 | break; 81 | case AudioManager.STATUS_PLAYING://播放中 82 | break; 83 | } 84 | } 85 | }).build(); 86 | } 87 | 88 | @Override 89 | protected void onDestroy() { 90 | super.onDestroy(); 91 | mAudioManager.release(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JessYanCoding/AudioManagerSDK/cf872a8513aaf3367f03cf86c594dab0a356e619/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JessYanCoding/AudioManagerSDK/cf872a8513aaf3367f03cf86c594dab0a356e619/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JessYanCoding/AudioManagerSDK/cf872a8513aaf3367f03cf86c594dab0a356e619/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JessYanCoding/AudioManagerSDK/cf872a8513aaf3367f03cf86c594dab0a356e619/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JessYanCoding/AudioManagerSDK/cf872a8513aaf3367f03cf86c594dab0a356e619/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AudioManager 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/me/jessyan/audiomanager/demo/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.demo; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /audiomanagersdk/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /audiomanagersdk/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.3" 6 | 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 23 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(dir: 'libs', include: ['*.jar']) 23 | testCompile 'junit:junit:4.12' 24 | compile 'com.android.support:appcompat-v7:23.4.0' 25 | compile 'com.github.piasy:rxandroidaudio:1.2.2' 26 | } 27 | -------------------------------------------------------------------------------- /audiomanagersdk/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/jess/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 | -------------------------------------------------------------------------------- /audiomanagersdk/src/androidTest/java/me/jessyan/audiomanager/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager; 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 | } -------------------------------------------------------------------------------- /audiomanagersdk/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/Audio.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager; 2 | 3 | 4 | import java.util.List; 5 | 6 | import me.jessyan.audiomanager.entity.AudioInfo; 7 | 8 | /** 9 | * Created by jess on 7/15/16. 10 | */ 11 | public interface Audio { 12 | 13 | 14 | /** 15 | * 开始录音 16 | */ 17 | void startRecord(); 18 | 19 | /** 20 | * 结束录音 21 | * 22 | * @return 返回该录音的id 23 | */ 24 | int stopRecord(); 25 | 26 | /** 27 | * 取消录制 28 | */ 29 | void cancel(); 30 | 31 | /** 32 | * 开始上传 33 | */ 34 | 35 | void startUpload(AudioInfo info, Post post); 36 | 37 | /** 38 | * 不指定具体的录音 39 | */ 40 | void startUpload(Post post); 41 | 42 | /** 43 | * 是否在录制 44 | * 45 | * @return 46 | */ 47 | boolean isRecord(); 48 | 49 | 50 | /** 51 | * 根据id获取相应的录音信息 52 | */ 53 | AudioInfo getAudioById(int id); 54 | 55 | /** 56 | * 增加到语音列表 57 | * @param info 58 | */ 59 | void addSpeech(T info); 60 | 61 | /** 62 | * 插入语音到指定位置 63 | * @param position 64 | * @param info 65 | */ 66 | void addSpeech(int position, T info); 67 | 68 | /** 69 | * 下载 70 | * @param info 71 | */ 72 | void download(T info); 73 | /** 74 | * 获得语音列表 75 | * @return 76 | */ 77 | List getSpeechList(); 78 | 79 | 80 | /** 81 | * 播放队列 82 | * @param info 83 | */ 84 | void play(T info); 85 | 86 | /** 87 | * 播放单个录音 88 | * @param info 89 | */ 90 | void palySingle(T info); 91 | 92 | /** 93 | * 停止播放 94 | */ 95 | void stopPlay(); 96 | 97 | /** 98 | * 恢复播放 99 | */ 100 | void resume(); 101 | 102 | 103 | 104 | /** 105 | * 释放资源 106 | */ 107 | void release(); 108 | 109 | 110 | } 111 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/AudioListener.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager; 2 | 3 | /** 4 | * Created by jess on 7/16/16. 5 | */ 6 | public interface AudioListener { 7 | /** 8 | * 录制时间太短 9 | */ 10 | void onRecordTooShort(); 11 | 12 | /** 13 | * 录制时间太长 14 | */ 15 | void onRecordTooLong(); 16 | 17 | /** 18 | * 录制时声音的振奋登记 19 | */ 20 | void onVoiceAmplitudeLevel(int level); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/AudioManager.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager; 2 | 3 | import android.content.Context; 4 | import android.media.MediaRecorder; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | import android.util.Log; 8 | 9 | import com.github.piasy.rxandroidaudio.AudioRecorder; 10 | import com.github.piasy.rxandroidaudio.PlayConfig; 11 | import com.github.piasy.rxandroidaudio.RxAmplitude; 12 | import com.github.piasy.rxandroidaudio.RxAudioPlayer; 13 | 14 | import java.io.File; 15 | import java.util.ArrayList; 16 | import java.util.LinkedList; 17 | import java.util.List; 18 | import java.util.Queue; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | import me.jessyan.audiomanager.entity.AudioInfo; 22 | import me.jessyan.audiomanager.io.Executor; 23 | import me.jessyan.audiomanager.io.HttpExecutor; 24 | import me.jessyan.audiomanager.io.HttpHelper; 25 | import me.jessyan.audiomanager.io.onDownloadListener; 26 | import me.jessyan.audiomanager.io.onNetWorkListener; 27 | import me.jessyan.audiomanager.util.DataHelper; 28 | import me.jessyan.audiomanager.util.LogUtils; 29 | import me.jessyan.audiomanager.util.UiUitls; 30 | import rx.Observable; 31 | import rx.Subscription; 32 | import rx.functions.Action1; 33 | import rx.functions.Func1; 34 | import rx.schedulers.Schedulers; 35 | 36 | /** 37 | * Created by jess on 7/15/16. 38 | */ 39 | public class AudioManager implements Audio, AudioRecorder.OnErrorListener { 40 | 41 | private final String TAG = getClass().getSimpleName(); 42 | private AudioRecorder mAudioRecorder; 43 | private RxAudioPlayer mRxAudioPlayer; 44 | private boolean isRecord; 45 | private Context mContext; 46 | private File mCurrentFile; 47 | private Queue mUploadFiles = new LinkedList<>(); 48 | private Queue mDownloadFiles = new LinkedList<>(); 49 | private List mNativeFiles = new ArrayList<>();//记录所有录制成功的audio 50 | private List mSpeechList = new ArrayList<>();//所有收到的列表 51 | private Queue mPlayQueue = new LinkedList<>();//播放音频的队列 52 | private int mMinRecordTime; 53 | private int mMaxRecordTime; 54 | private AudioListener mListener; 55 | private Subscription mSubscribe; 56 | private HttpHelper mHttpHelper; 57 | private Executor mExecutor; 58 | private StatusListener mStatusListener; 59 | private T mCurrentPlay; 60 | private boolean isPlaying; 61 | private boolean isStop; 62 | 63 | 64 | public static final int STATUS_DOWNLOADING = 0; 65 | public static final int STATUS_DOWNLOAD_FAILURE = 1; 66 | public static final int STATUS_DOWNLOAD_SUCCESS = 2; 67 | public static final int STATUS_PLAYING = 3; 68 | public static final int STATUS_PLAY_FAILURE = 4; 69 | public static final int STATUS_PLAY_COMPLETE = 5; 70 | public static final int STATUS_ADD_SPEECH_LIST = 6; 71 | public static final int STATUS_PLAY_PAUSE = 7; 72 | 73 | 74 | public static final int HANDLE_STATUS_TASK = 0; 75 | 76 | 77 | private Handler mHandler = new Handler() { 78 | @Override 79 | public void handleMessage(Message msg) { 80 | switch (msg.what) { 81 | case HANDLE_STATUS_TASK: 82 | T info = (T) msg.obj; 83 | if (mStatusListener != null) 84 | mStatusListener.onStatusChange(info, msg.arg1); 85 | break; 86 | } 87 | } 88 | }; 89 | private Subscription mPlaySubscription; 90 | private Subscription mSingleSubscription; 91 | 92 | 93 | private AudioManager(Context context, int maxRecordTime, int minRecordTime 94 | , AudioListener listener, HttpHelper httpHelper, StatusListener statusListener, Executor executor) { 95 | this.mContext = context; 96 | this.mMaxRecordTime = maxRecordTime; 97 | this.mMinRecordTime = minRecordTime; 98 | this.mListener = listener; 99 | this.mHttpHelper = httpHelper; 100 | this.mStatusListener = statusListener; 101 | mAudioRecorder = AudioRecorder.getInstance(); 102 | mRxAudioPlayer = RxAudioPlayer.getInstance(); 103 | mAudioRecorder.setOnErrorListener(this); 104 | mExecutor = executor; 105 | } 106 | 107 | public static Builder builder() { 108 | return new Builder(); 109 | } 110 | 111 | 112 | @Override 113 | public synchronized void startRecord() { 114 | this.isRecord = true;//标记 115 | mCurrentFile = new File(DataHelper.getCacheFile(mContext), System.nanoTime() + ".file.m4a"); 116 | 117 | // mAudioRecorder.prepareRecord(MediaRecorder.AudioSource.MIC, 118 | // MediaRecorder.OutputFormat.MPEG_4, MediaRecorder.AudioEncoder.AAC, 119 | // 192000, 192000, mCurrentFile); 120 | mSubscribe = Observable.just(1) 121 | .map(new Func1() { 122 | @Override 123 | public Object call(Integer integer) { 124 | mAudioRecorder.prepareRecord(MediaRecorder.AudioSource.MIC, 125 | MediaRecorder.OutputFormat.MPEG_4, MediaRecorder.AudioEncoder.AAC, 126 | 192000, 192000, mCurrentFile); 127 | return null; 128 | } 129 | }).flatMap(new Func1>() { 130 | @Override 131 | public Observable call(Object o) { 132 | return Observable.timer(1, TimeUnit.MILLISECONDS); 133 | } 134 | }).map(new Func1() { 135 | @Override 136 | public Object call(Long aLong) { 137 | mAudioRecorder.startRecord();//开始录音 138 | return null; 139 | } 140 | }).flatMap(new Func1>() { 141 | @Override 142 | public Observable call(Object o) { 143 | return RxAmplitude.from(mAudioRecorder); 144 | } 145 | }).subscribe(new Action1() { 146 | @Override 147 | public void call(Integer level) { 148 | if (mListener != null) { 149 | mListener.onVoiceAmplitudeLevel(level); 150 | // LogUtils.warnInfo(TAG, mAudioRecorder.progress() + "s"); 151 | if (mMaxRecordTime != -1) { 152 | if (mAudioRecorder.progress() == mMaxRecordTime) { 153 | mListener.onRecordTooLong(); 154 | } 155 | } 156 | } 157 | } 158 | }); 159 | 160 | 161 | // mSubscribe = RxAmplitude.from(mAudioRecorder) 162 | // .subscribe(new Action1() { 163 | // @Override 164 | // public void call(Integer level) { 165 | // if (mListener != null) { 166 | // mListener.onVoiceAmplitudeLevel(level); 167 | // if (mMaxRecordTime != -1) { 168 | // if (mAudioRecorder.progress() == mMaxRecordTime) { 169 | // mListener.onRecordTooLong(); 170 | // } 171 | // 172 | // } 173 | // } 174 | // } 175 | // }); 176 | } 177 | 178 | @Override 179 | public synchronized int stopRecord() { 180 | if (!isRecord) 181 | throw new IllegalStateException("not startRecord"); 182 | else 183 | this.isRecord = false; 184 | 185 | int record = mAudioRecorder.stopRecord(); 186 | if (record == -1) {//-1说明录制失败 187 | UiUitls.makeText(mContext, "录制发生错误"); 188 | return -1; 189 | } 190 | Log.w(TAG, record + "========"); 191 | if (mListener != null && mMinRecordTime != -1) {//录制时间太少 192 | if (record >= 0 && record <= mMinRecordTime) { 193 | mListener.onRecordTooShort(); 194 | return -1; 195 | } 196 | } 197 | AudioInfo audioInfo = new AudioInfo(mCurrentFile.getAbsolutePath(), record, System.currentTimeMillis()); 198 | mUploadFiles.offer(audioInfo); 199 | mNativeFiles.add(audioInfo); 200 | return mNativeFiles.indexOf(audioInfo);//返回录制的时间 201 | } 202 | 203 | 204 | /** 205 | * 取消录制 206 | */ 207 | @Override 208 | public synchronized void cancel() { 209 | if (!isRecord) 210 | throw new IllegalStateException("not startRecord"); 211 | else 212 | this.isRecord = false; 213 | mAudioRecorder.stopRecord(); 214 | } 215 | 216 | /** 217 | * 开始上传 218 | * 219 | * @param info 220 | */ 221 | @Override 222 | public void startUpload(AudioInfo info, Post post) { 223 | upload(info, post);//开始上传 224 | } 225 | 226 | 227 | @Override 228 | public void startUpload(Post post) { 229 | AudioInfo info = mUploadFiles.poll(); 230 | LogUtils.warnInfo(TAG, "upload_start------->" + info); 231 | if (info == null) return; 232 | upload(info, post);//开始上传 233 | } 234 | 235 | 236 | private void upload(final AudioInfo info, final Post post) { 237 | mHttpHelper.upload(info.filePath, new onNetWorkListener() { 238 | @Override 239 | public void onRequestSuccess(String url) { 240 | LogUtils.warnInfo(TAG, "success--------------->"); 241 | info.url = url; 242 | post.sendAudioMessage(info);//发送录音信息给主播 243 | } 244 | 245 | @Override 246 | public void onRequestFailure() { 247 | LogUtils.warnInfo(TAG, "failure--------------->"); 248 | mUploadFiles.offer(info); 249 | mExecutor.start(info, post);//失败重新上传 250 | } 251 | 252 | @Override 253 | public void onError(Throwable throwable) { 254 | LogUtils.warnInfo(TAG, "error--------------->"); 255 | mUploadFiles.offer(info); 256 | mExecutor.start(info, post);//失败重新上传 257 | } 258 | }); 259 | } 260 | 261 | @Override 262 | public boolean isRecord() { 263 | return isRecord; 264 | } 265 | 266 | @Override 267 | public AudioInfo getAudioById(int id) { 268 | return mNativeFiles.get(id); 269 | } 270 | 271 | @Override 272 | public void addSpeech(T info) { 273 | this.mSpeechList.add(info); 274 | SendStatusTask(info, STATUS_ADD_SPEECH_LIST); 275 | } 276 | 277 | @Override 278 | public void addSpeech(int position, T info) { 279 | this.mSpeechList.add(position, info); 280 | SendStatusTask(info, STATUS_ADD_SPEECH_LIST); 281 | } 282 | 283 | @Override 284 | public void download(final T info) { 285 | info.status = AudioInfo.DOWNLOADING; 286 | SendStatusTask(info, STATUS_DOWNLOADING); 287 | 288 | mHttpHelper.download(info.url, new onDownloadListener() { 289 | @Override 290 | public void onRequestSuccess(String filePath) { 291 | info.filePath = filePath; 292 | info.status = AudioInfo.NORMAL; 293 | SendStatusTask(info, STATUS_DOWNLOAD_SUCCESS); 294 | } 295 | 296 | @Override 297 | public void onRequestFailure() { 298 | info.status = AudioInfo.DOWNLOAD_FAILURE; 299 | SendStatusTask(info, STATUS_DOWNLOAD_FAILURE); 300 | } 301 | 302 | @Override 303 | public void onError(Throwable throwable) { 304 | info.status = AudioInfo.DOWNLOAD_FAILURE; 305 | SendStatusTask(info, STATUS_DOWNLOAD_FAILURE); 306 | } 307 | }); 308 | } 309 | 310 | /** 311 | * 发送执行改变状态的信息 312 | * 313 | * @param info 314 | * @param status 315 | */ 316 | private void SendStatusTask(T info, int status) { 317 | Message message = mHandler.obtainMessage(); 318 | message.what = HANDLE_STATUS_TASK; 319 | message.obj = info; 320 | message.arg1 = status; 321 | mHandler.sendMessage(message); 322 | } 323 | 324 | 325 | @Override 326 | public void release() { 327 | if (mSubscribe != null && !mSubscribe.isUnsubscribed()) { 328 | mSubscribe.unsubscribe(); 329 | mSubscribe = null; 330 | } 331 | if (mSingleSubscription != null && !mSingleSubscription.isUnsubscribed()) { 332 | mSingleSubscription.unsubscribe(); 333 | mSingleSubscription = null; 334 | } 335 | 336 | this.mCurrentFile = null; 337 | mListener = null; 338 | mStatusListener = null; 339 | mHttpHelper.release(); 340 | mExecutor.stop(); 341 | mHandler.removeCallbacksAndMessages(null); 342 | mHandler = null; 343 | // TODO: 7/19/16 清理所有缓存到本地的录音文件 344 | DataHelper.DeleteDir(DataHelper.getCacheFile(mContext)); 345 | } 346 | 347 | @Override 348 | public List getSpeechList() { 349 | return mSpeechList; 350 | } 351 | 352 | /** 353 | * 播放队列里面的录音 354 | * 355 | * @param playInfo 356 | */ 357 | @Override 358 | public synchronized void play(final T playInfo) { 359 | if (isStop) {//如果当前为停止状态,则放入队列 360 | LogUtils.warnInfo(TAG, "------------>stop"); 361 | mPlayQueue.offer(playInfo); 362 | } else {//否则执行播放 363 | LogUtils.warnInfo(TAG, "------------>play queue"); 364 | startPlayQueue(playInfo); 365 | } 366 | } 367 | 368 | /** 369 | * 播放单个语音,播放前停止所有正在执行的录音 370 | * 371 | * @param info 372 | */ 373 | @Override 374 | public synchronized void palySingle(final T info) { 375 | stopPlay();//播放前停止所有正在执行的录音 376 | File file = new File(info.filePath); 377 | if (!file.exists()) { 378 | throw new IllegalStateException("audio file isn't exist"); 379 | } 380 | mCurrentPlay = info;//当前正在执行的录音 381 | 382 | info.status = AudioInfo.PLAYING; 383 | SendStatusTask(info, STATUS_PLAYING);//播放中 384 | 385 | mSingleSubscription = mRxAudioPlayer.play(PlayConfig.file(file).build()) 386 | .subscribeOn(Schedulers.io()) 387 | .observeOn(Schedulers.immediate()) 388 | .subscribe(new Action1() { 389 | @Override 390 | public void call(Boolean aBoolean) { 391 | isStop = false; 392 | LogUtils.warnInfo(TAG, "----------palySingle success------------"); 393 | info.status = AudioInfo.NORMAL; 394 | SendStatusTask(info, STATUS_PLAY_COMPLETE);//播放完成 395 | 396 | } 397 | }, new Action1() { 398 | @Override 399 | public void call(Throwable throwable) { 400 | throwable.printStackTrace(); 401 | mPlayQueue.offer(info);//发生错误储存这个发生错误的录音 402 | isStop = false; 403 | LogUtils.warnInfo(TAG, "----------palySingle erro------------"); 404 | info.status = AudioInfo.NORMAL; 405 | SendStatusTask(info, STATUS_PLAY_FAILURE);//播放失败 406 | } 407 | }); 408 | } 409 | 410 | /** 411 | * 开始播放队列里的录音 412 | * 413 | * @param info 414 | */ 415 | private synchronized void startPlayQueue(final T info) { 416 | if (!isPlaying) {//没有正在播放的音频,才能播放下一个 417 | isPlaying = true; 418 | File file = new File(info.filePath); 419 | if (!file.exists()) { 420 | throw new IllegalStateException("audio file isn't exist"); 421 | } 422 | mCurrentPlay = info;//当前播放的录音 423 | 424 | info.status = AudioInfo.PLAYING; 425 | SendStatusTask(info, STATUS_PLAYING);//播放中 426 | 427 | mPlaySubscription = mRxAudioPlayer.play(PlayConfig.file(file).build()) 428 | .subscribeOn(Schedulers.io()) 429 | .observeOn(Schedulers.immediate()) 430 | .subscribe(new Action1() { 431 | @Override 432 | public void call(Boolean aBoolean) { 433 | isPlaying = false; 434 | info.status = AudioInfo.NORMAL; 435 | SendStatusTask(info, STATUS_PLAY_COMPLETE);//播放完成 436 | 437 | T nextInfo = mPlayQueue.poll();//拿出队列的值 438 | if (nextInfo != null) startPlayQueue(nextInfo);//如果队列里还有没有完成的任务则继续播放 439 | } 440 | }, new Action1() { 441 | @Override 442 | public void call(Throwable throwable) { 443 | throwable.printStackTrace(); 444 | mPlayQueue.offer(info);//发生错误储存这个发生错误的录音 445 | isPlaying = false; 446 | info.status = AudioInfo.NORMAL; 447 | SendStatusTask(info, STATUS_PLAY_FAILURE);//播放失败 448 | } 449 | }); 450 | } else {//如果正在运行则收回之前拿出来的任务 451 | mPlayQueue.offer(info); 452 | } 453 | } 454 | 455 | /** 456 | * 停止播放 457 | */ 458 | @Override 459 | public void stopPlay() { 460 | mRxAudioPlayer.stopPlay(); 461 | 462 | if (mPlaySubscription != null && !mPlaySubscription.isUnsubscribed())//停止当前播放的⌚️ 463 | mPlaySubscription.unsubscribe(); 464 | if (mSingleSubscription != null && !mSingleSubscription.isUnsubscribed()) 465 | mSingleSubscription.unsubscribe(); 466 | 467 | isStop = true; 468 | isPlaying = false; 469 | if (mCurrentPlay != null) { 470 | mCurrentPlay.status = AudioInfo.PLAY_PAUSE; 471 | SendStatusTask(mCurrentPlay, STATUS_PLAY_PAUSE);//播放暂停 472 | } 473 | } 474 | 475 | /** 476 | * 恢复播放 477 | */ 478 | @Override 479 | public void resume() { 480 | isStop = false; 481 | startPlayQueue(mCurrentPlay);//开始播放 482 | } 483 | 484 | 485 | @Override 486 | public void onError(@AudioRecorder.Error int error) { 487 | LogUtils.warnInfo(TAG, "--------------->" + error); 488 | } 489 | 490 | public static final class Builder { 491 | private Context mContext; 492 | private int mMinRecordTime = -1; 493 | private int mMaxRecordTime = -1; 494 | private AudioListener mListener; 495 | private StatusListener mStatusListener; 496 | private HttpHelper mHttpHelper; 497 | private Executor mExecutor = new HttpExecutor(mContext, mHttpHelper); 498 | 499 | private Builder() { 500 | } 501 | 502 | 503 | public Builder with(Context context) { 504 | if (context == null) 505 | throw new IllegalArgumentException("You cannot start a load on a null Context"); 506 | this.mContext = context; 507 | return this; 508 | } 509 | 510 | public Builder maxRecordTime(long timeout, TimeUnit unit) {//设置后会调用监听 511 | if (timeout < 0) throw new IllegalArgumentException("timeout < 0"); 512 | if (unit == null) throw new IllegalArgumentException("unit == null"); 513 | long seconds = unit.toSeconds(timeout); 514 | if (seconds > Integer.MAX_VALUE) 515 | throw new IllegalArgumentException("Timeout too large."); 516 | if (seconds == 0 && timeout > 0) 517 | throw new IllegalArgumentException("Timeout too small."); 518 | this.mMaxRecordTime = (int) seconds; 519 | return this; 520 | } 521 | 522 | public Builder minRecordTime(long timeout, TimeUnit unit) {//设置后会调用监听 523 | if (timeout < 0) throw new IllegalArgumentException("timeout < 0"); 524 | if (unit == null) throw new IllegalArgumentException("unit == null"); 525 | long seconds = unit.toSeconds(timeout); 526 | if (seconds > Integer.MAX_VALUE) 527 | throw new IllegalArgumentException("Timeout too large."); 528 | if (seconds == 0 && timeout > 0) 529 | throw new IllegalArgumentException("Timeout too small."); 530 | this.mMinRecordTime = (int) seconds; 531 | return this; 532 | } 533 | 534 | 535 | public Builder httpHelper(HttpHelper httpHelper) { 536 | this.mHttpHelper = httpHelper; 537 | return this; 538 | } 539 | 540 | public Builder executor(Executor executor) { 541 | this.mExecutor = executor; 542 | return this; 543 | } 544 | 545 | public Builder addAudioListener(AudioListener listener) { 546 | this.mListener = listener; 547 | return this; 548 | } 549 | 550 | public Builder addStatusListener(StatusListener listener) { 551 | this.mStatusListener = listener; 552 | return this; 553 | } 554 | 555 | 556 | public AudioManager build() { 557 | if (mContext == null) 558 | throw new IllegalStateException("context is required."); 559 | if (mHttpHelper == null) 560 | throw new IllegalStateException("HttpHelper is required"); 561 | 562 | return new AudioManager(mContext, mMaxRecordTime 563 | , mMinRecordTime, mListener, mHttpHelper 564 | , mStatusListener, mExecutor); 565 | } 566 | } 567 | } 568 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/Post.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager; 2 | 3 | import java.io.Serializable; 4 | 5 | import me.jessyan.audiomanager.entity.AudioInfo; 6 | 7 | /** 8 | * Created by jess on 7/26/16. 9 | */ 10 | public interface Post extends Serializable{ 11 | void sendAudioMessage(AudioInfo info); 12 | } 13 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/StatusListener.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager; 2 | 3 | import me.jessyan.audiomanager.entity.AudioInfo; 4 | 5 | /** 6 | * Created by jess on 7/21/16. 7 | */ 8 | public interface StatusListener { 9 | void onStatusChange(T info, int status); 10 | } 11 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/entity/AudioInfo.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.entity; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * Created by jess on 7/16/16. 7 | */ 8 | public class AudioInfo implements Serializable{ 9 | public static final int NORMAL = 0; 10 | public static final int DOWNLOADING = 1; 11 | public static final int DOWNLOAD_FAILURE = 2; 12 | public static final int PLAYING = 3; 13 | public static final int PLAY_PAUSE = 4; 14 | 15 | public String filePath; 16 | public String url; 17 | public int recordTime; 18 | public long ctime; 19 | public int status;//0.正常状态 1.下载中 2.播放失败 3.播放中 4.播放暂停 20 | 21 | public AudioInfo(){} 22 | public AudioInfo(String filePath, int recordTime,long ctime) { 23 | this.filePath = filePath; 24 | this.recordTime = recordTime; 25 | this.ctime = ctime; 26 | } 27 | 28 | public AudioInfo(String filePath, String url, int recordTime, long ctime) { 29 | this.filePath = filePath; 30 | this.url = url; 31 | this.recordTime = recordTime; 32 | this.ctime = ctime; 33 | } 34 | 35 | public AudioInfo(String filePath, String url, int recordTime, long ctime, int status) { 36 | this.filePath = filePath; 37 | this.url = url; 38 | this.recordTime = recordTime; 39 | this.ctime = ctime; 40 | this.status = status; 41 | } 42 | 43 | public int getStatus() { 44 | return status; 45 | } 46 | 47 | public void setStatus(int status) { 48 | this.status = status; 49 | } 50 | 51 | public String getFilePath() { 52 | return filePath; 53 | } 54 | 55 | public void setFilePath(String filePath) { 56 | this.filePath = filePath; 57 | } 58 | 59 | public String getUrl() { 60 | return url; 61 | } 62 | 63 | public void setUrl(String url) { 64 | this.url = url; 65 | } 66 | 67 | public int getRecordTime() { 68 | return recordTime; 69 | } 70 | 71 | public void setRecordTime(int recordTime) { 72 | this.recordTime = recordTime; 73 | } 74 | 75 | public long getCtime() { 76 | return ctime; 77 | } 78 | 79 | public void setCtime(long ctime) { 80 | this.ctime = ctime; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/io/Executor.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.io; 2 | 3 | import me.jessyan.audiomanager.Post; 4 | import me.jessyan.audiomanager.entity.AudioInfo; 5 | 6 | /** 7 | * Created by jess on 8/23/16 16:12 8 | * Contact with jess.yan.effort@gmail.com 9 | */ 10 | public interface Executor { 11 | /** 12 | * 开始执行 13 | * @param info 14 | * @param post 15 | */ 16 | void start(AudioInfo info, Post post); 17 | /** 18 | * 停止 19 | */ 20 | void stop(); 21 | } 22 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/io/HttpExecutor.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.io; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.os.Message; 7 | 8 | import me.jessyan.audiomanager.Post; 9 | import me.jessyan.audiomanager.entity.AudioInfo; 10 | import me.jessyan.audiomanager.util.DeviceUtils; 11 | import me.jessyan.audiomanager.util.LogUtils; 12 | 13 | /** 14 | * Created by jess on 7/20/16. 15 | */ 16 | public class HttpExecutor implements Executor{ 17 | private final String TAG = getClass().getSimpleName(); 18 | private HttpHelper mHelper; 19 | private Context mContext; 20 | public static final int DELAY = 5_000; 21 | 22 | public HttpExecutor(Context context, HttpHelper mHelper) { 23 | this.mHelper = mHelper; 24 | this.mContext = context; 25 | } 26 | 27 | Handler mHandler = new Handler() { 28 | @Override 29 | public void handleMessage(Message msg) { 30 | switch (msg.what) { 31 | case 0: 32 | execute(msg); 33 | break; 34 | } 35 | } 36 | }; 37 | 38 | private void execute(Message msg) { 39 | // final AudioInfo info = mUploadQueue.poll(); 40 | // if (info != null) { 41 | // if (!DeviceUtils.netIsConnected(mContext)) {//如果没有网络则5s在执行 42 | // LogUtils.warnInfo(TAG, "not net--------------->"); 43 | // mUploadQueue.offer(info); 44 | // mHandler.sendEmptyMessageDelayed(0, DELAY); 45 | // return; 46 | // } 47 | // mHelper.upload(info.filePath, new onNetWorkListener() { 48 | // @Override 49 | // public void onRequestSuccess(String url) { 50 | // LogUtils.warnInfo(TAG, "success--------------->"); 51 | // info.url = url; 52 | // mManager.sendRecordMessageToPresenter(info); 53 | // } 54 | // 55 | // @Override 56 | // public void onRequestFailure() { 57 | // LogUtils.warnInfo(TAG, "failure--------------->"); 58 | // mUploadQueue.offer(info); 59 | // mHandler.sendEmptyMessage(0); 60 | // } 61 | // 62 | // @Override 63 | // public void onError(Throwable throwable) { 64 | // LogUtils.warnInfo(TAG, "error--------------->"); 65 | // mUploadQueue.offer(info); 66 | // mHandler.sendEmptyMessage(0); 67 | // } 68 | // }); 69 | // } 70 | 71 | 72 | final Bundle bundle = (Bundle) msg.obj; 73 | 74 | if (!DeviceUtils.netIsConnected(mContext)) {//如果没有网络则5s后在执行 75 | LogUtils.warnInfo(TAG, "not net--------------->"); 76 | sendMessage(bundle, DELAY); 77 | return; 78 | } 79 | 80 | final AudioInfo audioInfo = (AudioInfo) bundle.getSerializable("info"); 81 | mHelper.upload(audioInfo.filePath, new onNetWorkListener() { 82 | @Override 83 | public void onRequestSuccess(String url) { 84 | LogUtils.warnInfo(TAG, "success--------------->"); 85 | Post post = (Post) bundle.getSerializable("post"); 86 | audioInfo.url = url; 87 | post.sendAudioMessage(audioInfo); 88 | } 89 | 90 | @Override 91 | public void onRequestFailure() { 92 | LogUtils.warnInfo(TAG, "failure--------------->"); 93 | sendMessage(bundle, 0); 94 | } 95 | 96 | @Override 97 | public void onError(Throwable throwable) { 98 | LogUtils.warnInfo(TAG, "error--------------->"); 99 | sendMessage(bundle, 0); 100 | } 101 | }); 102 | 103 | 104 | } 105 | 106 | public void start(AudioInfo info, Post post) { 107 | // mHandler.sendEmptyMessage(0); 108 | 109 | 110 | Bundle bundle = new Bundle(); 111 | bundle.putSerializable("info", info); 112 | bundle.putSerializable("post", post); 113 | 114 | sendMessage(bundle, 0); 115 | } 116 | 117 | /** 118 | * 发送信息 119 | * 120 | * @param delay 0为不延迟 121 | */ 122 | private void sendMessage(Bundle bundle, int delay) { 123 | 124 | Message message = mHandler.obtainMessage(); 125 | message.what = 0; 126 | message.obj = bundle; 127 | if (delay == 0) 128 | mHandler.sendMessage(message); 129 | else 130 | mHandler.sendMessageDelayed(message, delay); 131 | } 132 | 133 | public void stop() { 134 | mHandler.removeCallbacksAndMessages(null); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/io/HttpHelper.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.io; 2 | 3 | /** 4 | * Created by jess on 7/18/16. 5 | */ 6 | public interface HttpHelper { 7 | 8 | void upload(String filePath, onNetWorkListener listener); 9 | 10 | void download(String url, onDownloadListener listener); 11 | 12 | void release(); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/io/onDownloadListener.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.io; 2 | 3 | /** 4 | * Created by jess on 7/21/16. 5 | */ 6 | public interface onDownloadListener { 7 | void onRequestSuccess(String filePath); 8 | void onRequestFailure(); 9 | void onError(Throwable throwable); 10 | } 11 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/io/onNetWorkListener.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.io; 2 | 3 | /** 4 | * Created by jess on 7/20/16. 5 | */ 6 | public interface onNetWorkListener { 7 | void onRequestSuccess(String url); 8 | void onRequestFailure(); 9 | void onError(Throwable throwable); 10 | } 11 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/util/DataHelper.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.util; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | 6 | import java.io.File; 7 | 8 | /** 9 | * Created by jess on 7/16/16. 10 | */ 11 | public class DataHelper { 12 | public static final String CACHE_FILE_PATH = "/mnt/sdcard/zhibo_cache/record"; 13 | 14 | 15 | /** 16 | * 返回缓存文件夹 17 | */ 18 | public static File getCacheFile(Context context) { 19 | File file = null; 20 | if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 21 | file = context.getExternalCacheDir();//获取系统管理的sd卡缓存文件 22 | if (file == null) {//如果获取的为空,就是用自己定义的缓存文件夹做缓存路径 23 | file = new File(CACHE_FILE_PATH); 24 | } else { 25 | file = new File(file, "record"); 26 | } 27 | if (!file.exists()) { 28 | file.mkdirs(); 29 | } 30 | 31 | return file; 32 | } else { 33 | file = new File(context.getCacheDir(), "record"); 34 | if (!file.exists()) { 35 | file.mkdirs(); 36 | } 37 | return file; 38 | } 39 | } 40 | 41 | /** 42 | * 使用递归删除文件夹 43 | * 44 | * @param dir 45 | * @return 46 | */ 47 | public static boolean DeleteDir(File dir) { 48 | if (dir == null) { 49 | return false; 50 | } 51 | if (!dir.isDirectory()) { 52 | return false; 53 | } 54 | File[] files = dir.listFiles(); 55 | for (File file : files) { 56 | if (file.isFile()) { 57 | file.delete(); 58 | } else if (file.isDirectory()) { 59 | DeleteDir(file); // 递归调用继续统计 60 | } 61 | } 62 | return true; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/util/DeviceUtils.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.util; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.app.Dialog; 6 | import android.content.ActivityNotFoundException; 7 | import android.content.ClipboardManager; 8 | import android.content.ComponentName; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.pm.PackageInfo; 12 | import android.content.pm.PackageManager; 13 | import android.content.pm.ResolveInfo; 14 | import android.graphics.Point; 15 | import android.net.ConnectivityManager; 16 | import android.net.NetworkInfo; 17 | import android.net.Uri; 18 | import android.os.Build; 19 | import android.os.Environment; 20 | import android.os.PowerManager; 21 | import android.telephony.TelephonyManager; 22 | import android.text.TextUtils; 23 | import android.util.DisplayMetrics; 24 | import android.util.Log; 25 | import android.view.Display; 26 | import android.view.View; 27 | import android.view.ViewConfiguration; 28 | import android.view.WindowManager; 29 | import android.view.inputmethod.InputMethodManager; 30 | 31 | import java.io.File; 32 | import java.lang.reflect.Field; 33 | import java.text.NumberFormat; 34 | import java.util.List; 35 | 36 | /** 37 | * 常用信息工具类 38 | * Created by jungle on 16/5/21. 39 | * com.zhiyicx.zhibo.util 40 | * zhibo_android 41 | * email:335891510@qq.com 42 | */ 43 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 44 | public class DeviceUtils { 45 | // 手机网络类型 46 | public static final int NETTYPE_WIFI = 0x01; 47 | public static final int NETTYPE_CMWAP = 0x02; 48 | public static final int NETTYPE_CMNET = 0x03; 49 | 50 | public static boolean GTE_HC; 51 | public static boolean GTE_ICS; 52 | public static boolean PRE_HC; 53 | private static Boolean _hasBigScreen = null; 54 | private static Boolean _hasCamera = null; 55 | private static Boolean _isTablet = null; 56 | private static Integer _loadFactor = null; 57 | public static float displayDensity = 0.0F; 58 | 59 | static { 60 | GTE_ICS = Build.VERSION.SDK_INT >= 14; 61 | GTE_HC = Build.VERSION.SDK_INT >= 11; 62 | PRE_HC = Build.VERSION.SDK_INT < 11; 63 | } 64 | 65 | public DeviceUtils() { 66 | } 67 | 68 | /** 69 | * dp转px 70 | * 71 | * @param context 72 | * @param dp 73 | * @return 74 | */ 75 | public static float dpToPixel(Context context, float dp) { 76 | return dp * (getDisplayMetrics(context).densityDpi / 160F); 77 | } 78 | 79 | /** 80 | * px转dp 81 | * 82 | * @param context 83 | * @param f 84 | * @return 85 | */ 86 | public static float pixelsToDp(Context context, float f) { 87 | return f / (getDisplayMetrics(context).densityDpi / 160F); 88 | } 89 | 90 | public static int getDefaultLoadFactor(Context context) { 91 | if (_loadFactor == null) { 92 | Integer integer = Integer.valueOf(0xf & context 93 | .getResources().getConfiguration().screenLayout); 94 | _loadFactor = integer; 95 | _loadFactor = Integer.valueOf(Math.max(integer.intValue(), 1)); 96 | } 97 | return _loadFactor.intValue(); 98 | } 99 | 100 | public static float getDensity(Context context) { 101 | if (displayDensity == 0.0) 102 | displayDensity = getDisplayMetrics(context).density; 103 | return displayDensity; 104 | } 105 | 106 | public static DisplayMetrics getDisplayMetrics(Context context) { 107 | DisplayMetrics displaymetrics = new DisplayMetrics(); 108 | ((WindowManager) context.getSystemService( 109 | Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics( 110 | displaymetrics); 111 | return displaymetrics; 112 | } 113 | 114 | /** 115 | * 屏幕高度 116 | * 117 | * @param context 118 | * @return 119 | */ 120 | public static float getScreenHeight(Context context) { 121 | return getDisplayMetrics(context).heightPixels; 122 | } 123 | 124 | /** 125 | * 屏幕宽度 126 | * 127 | * @param context 128 | * @return 129 | */ 130 | public static float getScreenWidth(Context context) { 131 | return getDisplayMetrics(context).widthPixels; 132 | } 133 | 134 | /** 135 | * 获取activity尺寸 136 | * 137 | * @param activity 138 | * @return 139 | */ 140 | public static int[] getRealScreenSize(Activity activity) { 141 | int[] size = new int[2]; 142 | int screenWidth = 0, screenHeight = 0; 143 | WindowManager w = activity.getWindowManager(); 144 | Display d = w.getDefaultDisplay(); 145 | DisplayMetrics metrics = new DisplayMetrics(); 146 | d.getMetrics(metrics); 147 | // since SDK_INT = 1; 148 | screenWidth = metrics.widthPixels; 149 | screenHeight = metrics.heightPixels; 150 | // includes window decorations (statusbar bar/menu bar) 151 | if (Build.VERSION.SDK_INT >= 14 && Build.VERSION.SDK_INT < 17) 152 | try { 153 | screenWidth = (Integer) Display.class.getMethod("getRawWidth") 154 | .invoke(d); 155 | screenHeight = (Integer) Display.class 156 | .getMethod("getRawHeight").invoke(d); 157 | } catch (Exception ignored) { 158 | } 159 | // includes window decorations (statusbar bar/menu bar) 160 | if (Build.VERSION.SDK_INT >= 17) 161 | try { 162 | Point realSize = new Point(); 163 | Display.class.getMethod("getRealSize", Point.class).invoke(d, 164 | realSize); 165 | screenWidth = realSize.x; 166 | screenHeight = realSize.y; 167 | } catch (Exception ignored) { 168 | } 169 | size[0] = screenWidth; 170 | size[1] = screenHeight; 171 | return size; 172 | } 173 | 174 | /** 175 | * 获取状态栏高度 176 | * 177 | * @param context 178 | * @return 179 | */ 180 | public static int getStatusBarHeight(Context context) { 181 | Class c = null; 182 | Object obj = null; 183 | Field field = null; 184 | int x = 0; 185 | try { 186 | c = Class.forName("com.android.internal.R$dimen"); 187 | obj = c.newInstance(); 188 | field = c.getField("status_bar_height"); 189 | x = Integer.parseInt(field.get(obj).toString()); 190 | return context.getResources() 191 | .getDimensionPixelSize(x); 192 | } catch (Exception e) { 193 | e.printStackTrace(); 194 | } 195 | return 0; 196 | } 197 | 198 | 199 | public static boolean hasBigScreen(Context context) { 200 | boolean flag = true; 201 | if (_hasBigScreen == null) { 202 | boolean flag1; 203 | if ((0xf & context.getResources() 204 | .getConfiguration().screenLayout) >= 3) 205 | flag1 = flag; 206 | else 207 | flag1 = false; 208 | Boolean boolean1 = Boolean.valueOf(flag1); 209 | _hasBigScreen = boolean1; 210 | if (!boolean1.booleanValue()) { 211 | if (getDensity(context) <= 1.5F) 212 | flag = false; 213 | _hasBigScreen = Boolean.valueOf(flag); 214 | } 215 | } 216 | return _hasBigScreen.booleanValue(); 217 | } 218 | 219 | /** 220 | * 设备是否有相机 221 | * 222 | * @param context 223 | * @return 224 | */ 225 | public static final boolean hasCamera(Context context) { 226 | if (_hasCamera == null) { 227 | PackageManager pckMgr = context 228 | .getPackageManager(); 229 | boolean flag = pckMgr 230 | .hasSystemFeature("android.hardware.camera.front"); 231 | boolean flag1 = pckMgr.hasSystemFeature("android.hardware.camera"); 232 | boolean flag2; 233 | if (flag || flag1) 234 | flag2 = true; 235 | else 236 | flag2 = false; 237 | _hasCamera = Boolean.valueOf(flag2); 238 | } 239 | return _hasCamera.booleanValue(); 240 | } 241 | 242 | /** 243 | * 设备是否有实体菜单 244 | * 245 | * @param context 246 | * @return 247 | */ 248 | public static boolean hasHardwareMenuKey(Context context) { 249 | boolean flag = false; 250 | if (PRE_HC) 251 | flag = true; 252 | else if (GTE_ICS) { 253 | flag = ViewConfiguration.get(context).hasPermanentMenuKey(); 254 | } else 255 | flag = false; 256 | return flag; 257 | } 258 | 259 | /** 260 | * 当前是否有网 261 | * 262 | * @param context 263 | * @return 264 | */ 265 | public static boolean hasInternet(Context context) { 266 | boolean flag; 267 | ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 268 | if (manager != null && manager.getActiveNetworkInfo() != null) 269 | flag = true; 270 | else 271 | flag = false; 272 | return flag; 273 | } 274 | 275 | /** 276 | * 当前的包是否存在 277 | * 278 | * @param context 279 | * @param pckName 280 | * @return 281 | */ 282 | public static boolean isPackageExist(Context context, String pckName) { 283 | try { 284 | PackageInfo pckInfo = context.getPackageManager() 285 | .getPackageInfo(pckName, 0); 286 | if (pckInfo != null) 287 | return true; 288 | } catch (PackageManager.NameNotFoundException e) { 289 | Log.e("TDvice", e.getMessage()); 290 | } 291 | return false; 292 | } 293 | 294 | public static void hideAnimatedView(View view) { 295 | if (PRE_HC && view != null) 296 | view.setPadding(view.getWidth(), 0, 0, 0); 297 | } 298 | 299 | /** 300 | * 隐藏软键盘 301 | * 302 | * @param context 303 | * @param view 304 | */ 305 | public static void hideSoftKeyboard(Context context, View view) { 306 | if (view == null) 307 | return; 308 | InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService( 309 | Context.INPUT_METHOD_SERVICE); 310 | if (inputMethodManager.isActive()) 311 | inputMethodManager.hideSoftInputFromWindow( 312 | view.getWindowToken(), 0); 313 | } 314 | 315 | /** 316 | * 是否是横屏 317 | * 318 | * @param context 319 | * @return 320 | */ 321 | public static boolean isLandscape(Context context) { 322 | boolean flag; 323 | if (context.getResources().getConfiguration().orientation == 2) 324 | flag = true; 325 | else 326 | flag = false; 327 | return flag; 328 | } 329 | 330 | /** 331 | * 是否是竖屏 332 | * 333 | * @param context 334 | * @return 335 | */ 336 | public static boolean isPortrait(Context context) { 337 | boolean flag = true; 338 | if (context.getResources().getConfiguration().orientation != 1) 339 | flag = false; 340 | return flag; 341 | } 342 | 343 | public static boolean isTablet(Context context) { 344 | if (_isTablet == null) { 345 | boolean flag; 346 | if ((0xf & context.getResources() 347 | .getConfiguration().screenLayout) >= 3) 348 | flag = true; 349 | else 350 | flag = false; 351 | _isTablet = Boolean.valueOf(flag); 352 | } 353 | return _isTablet.booleanValue(); 354 | } 355 | 356 | 357 | public static void showAnimatedView(View view) { 358 | if (PRE_HC && view != null) 359 | view.setPadding(0, 0, 0, 0); 360 | } 361 | 362 | public static void showSoftKeyboard(Dialog dialog) { 363 | dialog.getWindow().setSoftInputMode(4); 364 | } 365 | 366 | public static void showSoftKeyboard(Context context, View view) { 367 | ((InputMethodManager) context.getSystemService( 368 | Context.INPUT_METHOD_SERVICE)).showSoftInput(view, 369 | InputMethodManager.SHOW_FORCED); 370 | } 371 | 372 | public static void toogleSoftKeyboard(Context context, View view) { 373 | ((InputMethodManager) context.getSystemService( 374 | Context.INPUT_METHOD_SERVICE)).toggleSoftInput(0, 375 | InputMethodManager.HIDE_NOT_ALWAYS); 376 | } 377 | 378 | public static boolean isSdcardReady() { 379 | return Environment.MEDIA_MOUNTED.equals(Environment 380 | .getExternalStorageState()); 381 | } 382 | 383 | public static String getCurCountryLan(Context context) { 384 | return context.getResources().getConfiguration().locale 385 | .getLanguage() 386 | + "-" 387 | + context.getResources().getConfiguration().locale 388 | .getCountry(); 389 | } 390 | 391 | public static boolean isZhCN(Context context) { 392 | String lang = context.getResources() 393 | .getConfiguration().locale.getCountry(); 394 | if (lang.equalsIgnoreCase("CN")) { 395 | return true; 396 | } 397 | return false; 398 | } 399 | 400 | public static String percent(double p1, double p2) { 401 | String str; 402 | double p3 = p1 / p2; 403 | NumberFormat nf = NumberFormat.getPercentInstance(); 404 | nf.setMinimumFractionDigits(2); 405 | str = nf.format(p3); 406 | return str; 407 | } 408 | 409 | public static String percent2(double p1, double p2) { 410 | String str; 411 | double p3 = p1 / p2; 412 | NumberFormat nf = NumberFormat.getPercentInstance(); 413 | nf.setMinimumFractionDigits(0); 414 | str = nf.format(p3); 415 | return str; 416 | } 417 | 418 | 419 | public static boolean isHaveMarket(Context context) { 420 | Intent intent = new Intent(); 421 | intent.setAction("android.intent.action.MAIN"); 422 | intent.addCategory("android.intent.category.APP_MARKET"); 423 | PackageManager pm = context.getPackageManager(); 424 | List infos = pm.queryIntentActivities(intent, 0); 425 | return infos.size() > 0; 426 | } 427 | 428 | public static void setFullScreen(Activity activity) { 429 | WindowManager.LayoutParams params = activity.getWindow() 430 | .getAttributes(); 431 | params.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN; 432 | activity.getWindow().setAttributes(params); 433 | activity.getWindow().addFlags( 434 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 435 | } 436 | 437 | public static void cancelFullScreen(Activity activity) { 438 | WindowManager.LayoutParams params = activity.getWindow() 439 | .getAttributes(); 440 | params.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN); 441 | activity.getWindow().setAttributes(params); 442 | activity.getWindow().clearFlags( 443 | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); 444 | } 445 | 446 | public static PackageInfo getPackageInfo(Context context, String pckName) { 447 | try { 448 | return context.getPackageManager() 449 | .getPackageInfo(pckName, 0); 450 | } catch (PackageManager.NameNotFoundException e) { 451 | e.printStackTrace(); 452 | } 453 | return null; 454 | } 455 | 456 | /** 457 | * 获取版本号 458 | * 459 | * @param context 460 | * @return 461 | */ 462 | public static int getVersionCode(Context context) { 463 | int versionCode = 0; 464 | try { 465 | versionCode = context.getPackageManager() 466 | .getPackageInfo(context.getPackageName(), 467 | 0).versionCode; 468 | } catch (PackageManager.NameNotFoundException ex) { 469 | versionCode = 0; 470 | } 471 | return versionCode; 472 | } 473 | 474 | /** 475 | * 获取指定包名应用的版本号 476 | * 477 | * @param context 478 | * @param packageName 479 | * @return 480 | */ 481 | public static int getVersionCode(Context context, String packageName) { 482 | int versionCode = 0; 483 | try { 484 | versionCode = context.getPackageManager() 485 | .getPackageInfo(packageName, 0).versionCode; 486 | } catch (PackageManager.NameNotFoundException ex) { 487 | versionCode = 0; 488 | } 489 | return versionCode; 490 | } 491 | 492 | /** 493 | * 获取版本名 494 | * 495 | * @param context 496 | * @return 497 | */ 498 | public static String getVersionName(Context context) { 499 | String name = ""; 500 | try { 501 | name = context.getPackageManager() 502 | .getPackageInfo(context.getPackageName(), 503 | 0).versionName; 504 | } catch (PackageManager.NameNotFoundException ex) { 505 | name = ""; 506 | } 507 | return name; 508 | } 509 | 510 | public static boolean isScreenOn(Context context) { 511 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 512 | return pm.isScreenOn(); 513 | } 514 | 515 | /** 516 | * 安装应用 517 | * 518 | * @param context 519 | * @param file 520 | */ 521 | public static void installAPK(Context context, File file) { 522 | if (file == null || !file.exists()) 523 | return; 524 | Intent intent = new Intent(); 525 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 526 | intent.setAction(Intent.ACTION_VIEW); 527 | intent.setDataAndType(Uri.fromFile(file), 528 | "application/vnd.android.package-archive"); 529 | context.startActivity(intent); 530 | } 531 | 532 | public static Intent getInstallApkIntent(File file) { 533 | Intent intent = new Intent(); 534 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 535 | intent.setAction(Intent.ACTION_VIEW); 536 | intent.setDataAndType(Uri.fromFile(file), 537 | "application/vnd.android.package-archive"); 538 | return intent; 539 | } 540 | 541 | /** 542 | * 拨打电话 543 | * 544 | * @param context 545 | * @param number 546 | */ 547 | public static void openDial(Context context, String number) { 548 | Uri uri = Uri.parse("tel:" + number); 549 | Intent it = new Intent(Intent.ACTION_DIAL, uri); 550 | context.startActivity(it); 551 | } 552 | 553 | public static void openSMS(Context context, String smsBody, String tel) { 554 | Uri uri = Uri.parse("smsto:" + tel); 555 | Intent it = new Intent(Intent.ACTION_SENDTO, uri); 556 | it.putExtra("sms_body", smsBody); 557 | context.startActivity(it); 558 | } 559 | 560 | public static void openDail(Context context) { 561 | Intent intent = new Intent(Intent.ACTION_DIAL); 562 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 563 | context.startActivity(intent); 564 | } 565 | 566 | public static void openSendMsg(Context context) { 567 | Uri uri = Uri.parse("smsto:"); 568 | Intent intent = new Intent(Intent.ACTION_SENDTO, uri); 569 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 570 | context.startActivity(intent); 571 | } 572 | 573 | public static void openCamera(Context context) { 574 | Intent intent = new Intent(); // 调用照相机 575 | intent.setAction("android.media.action.STILL_IMAGE_CAMERA"); 576 | intent.setFlags(0x34c40000); 577 | context.startActivity(intent); 578 | } 579 | 580 | public static String getIMEI(Context context) { 581 | TelephonyManager tel = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 582 | return tel.getDeviceId(); 583 | } 584 | 585 | public static String getPhoneType() { 586 | return Build.MODEL; 587 | } 588 | 589 | public static void openApp(Context context, String packageName) { 590 | Intent mainIntent = context.getPackageManager() 591 | .getLaunchIntentForPackage(packageName); 592 | if (mainIntent == null) { 593 | mainIntent = new Intent(packageName); 594 | } else { 595 | } 596 | context.startActivity(mainIntent); 597 | } 598 | 599 | public static boolean openAppActivity(Context context, String packageName, 600 | String activityName) { 601 | Intent intent = new Intent(Intent.ACTION_MAIN); 602 | intent.addCategory(Intent.CATEGORY_LAUNCHER); 603 | ComponentName cn = new ComponentName(packageName, activityName); 604 | intent.setComponent(cn); 605 | try { 606 | context.startActivity(intent); 607 | return true; 608 | } catch (Exception e) { 609 | return false; 610 | } 611 | } 612 | 613 | /** 614 | * wifi是否开启 615 | * 616 | * @param context 617 | * @return 618 | */ 619 | public static boolean isWifiOpen(Context context) { 620 | boolean isWifiConnect = false; 621 | ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 622 | // check the networkInfos numbers 623 | NetworkInfo[] networkInfos = cm.getAllNetworkInfo(); 624 | for (int i = 0; i < networkInfos.length; i++) { 625 | if (networkInfos[i].getState() == NetworkInfo.State.CONNECTED) { 626 | if (networkInfos[i].getType() == ConnectivityManager.TYPE_MOBILE) { 627 | isWifiConnect = false; 628 | } 629 | if (networkInfos[i].getType() == ConnectivityManager.TYPE_WIFI) { 630 | isWifiConnect = true; 631 | } 632 | } 633 | } 634 | return isWifiConnect; 635 | } 636 | 637 | /** 638 | * 卸载软件 639 | * 640 | * @param context 641 | * @param packageName 642 | */ 643 | public static void uninstallApk(Context context, String packageName) { 644 | if (isPackageExist(context, packageName)) { 645 | Uri packageURI = Uri.parse("package:" + packageName); 646 | Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, 647 | packageURI); 648 | context.startActivity(uninstallIntent); 649 | } 650 | } 651 | 652 | @SuppressWarnings("deprecation") 653 | public static void copyTextToBoard(Context context, String string) { 654 | if (TextUtils.isEmpty(string)) 655 | return; 656 | ClipboardManager clip = (ClipboardManager) context 657 | .getSystemService(Context.CLIPBOARD_SERVICE); 658 | clip.setText(string); 659 | } 660 | 661 | /** 662 | * 发送邮件 663 | * 664 | * @param context 665 | * @param subject 主题 666 | * @param content 内容 667 | * @param emails 邮件地址 668 | */ 669 | public static void sendEmail(Context context, String subject, 670 | String content, String... emails) { 671 | try { 672 | Intent intent = new Intent(Intent.ACTION_SEND); 673 | // 模拟器 674 | // intent.setType("text/plain"); 675 | intent.setType("message/rfc822"); // 真机 676 | intent.putExtra(Intent.EXTRA_EMAIL, emails); 677 | intent.putExtra(Intent.EXTRA_SUBJECT, subject); 678 | intent.putExtra(Intent.EXTRA_TEXT, content); 679 | context.startActivity(intent); 680 | } catch (ActivityNotFoundException e) { 681 | e.printStackTrace(); 682 | } 683 | } 684 | 685 | public static int getStatuBarHeight(Context context) { 686 | Class c = null; 687 | Object obj = null; 688 | Field field = null; 689 | int x = 0, sbar = 38;// 默认为38,貌似大部分是这样的 690 | try { 691 | c = Class.forName("com.android.internal.R$dimen"); 692 | obj = c.newInstance(); 693 | field = c.getField("status_bar_height"); 694 | x = Integer.parseInt(field.get(obj).toString()); 695 | sbar = context.getResources() 696 | .getDimensionPixelSize(x); 697 | 698 | } catch (Exception e1) { 699 | e1.printStackTrace(); 700 | } 701 | return sbar; 702 | } 703 | 704 | 705 | public static boolean hasStatusBar(Activity activity) { 706 | WindowManager.LayoutParams attrs = activity.getWindow().getAttributes(); 707 | if ((attrs.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == WindowManager.LayoutParams.FLAG_FULLSCREEN) { 708 | return false; 709 | } else { 710 | return true; 711 | } 712 | } 713 | 714 | /** 715 | * 调用系统安装了的应用分享 716 | * 717 | * @param context 718 | * @param title 719 | * @param url 720 | */ 721 | public static void showSystemShareOption(Activity context, 722 | final String title, final String url) { 723 | Intent intent = new Intent(Intent.ACTION_SEND); 724 | intent.setType("text/plain"); 725 | intent.putExtra(Intent.EXTRA_SUBJECT, "分享:" + title); 726 | intent.putExtra(Intent.EXTRA_TEXT, title + " " + url); 727 | context.startActivity(Intent.createChooser(intent, "选择分享")); 728 | } 729 | 730 | /** 731 | * 获取当前网络类型 732 | * 733 | * @return 0:没有网络 1:WIFI网络 2:WAP网络 3:NET网络 734 | */ 735 | public static int getNetworkType(Context context) { 736 | int netType = 0; 737 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 738 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); 739 | if (networkInfo == null) { 740 | return netType; 741 | } 742 | int nType = networkInfo.getType(); 743 | if (nType == ConnectivityManager.TYPE_MOBILE) { 744 | String extraInfo = networkInfo.getExtraInfo(); 745 | if (extraInfo != null && !extraInfo.isEmpty()) { 746 | if (extraInfo.toLowerCase().equals("cmnet")) { 747 | netType = NETTYPE_CMNET; 748 | } else { 749 | netType = NETTYPE_CMWAP; 750 | } 751 | } 752 | } else if (nType == ConnectivityManager.TYPE_WIFI) { 753 | netType = NETTYPE_WIFI; 754 | } 755 | return netType; 756 | } 757 | 758 | public static boolean netIsConnected(Context context) { 759 | ConnectivityManager connectMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 760 | //手机网络连接状态 761 | NetworkInfo mobNetInfo = connectMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE); 762 | //WIFI连接状态 763 | NetworkInfo wifiNetInfo = connectMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI); 764 | if (!mobNetInfo.isConnected() && !wifiNetInfo.isConnected()) { 765 | //当前无可用的网络 766 | return false; 767 | } 768 | return true; 769 | } 770 | 771 | /** 772 | * 判断是否存在sd卡 773 | * 774 | * @return 775 | */ 776 | public static boolean isExitsSdcard() { 777 | if (Environment.getExternalStorageState().equals( 778 | Environment.MEDIA_MOUNTED)) 779 | return true; 780 | else 781 | return false; 782 | } 783 | 784 | 785 | } 786 | 787 | 788 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/util/LogUtils.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.util; 2 | 3 | import android.text.TextUtils; 4 | import android.util.Log; 5 | 6 | /** 7 | * Created by jungle on 16/5/20. 8 | * com.zhiyicx.zhibo.util 9 | * zhibo_android 10 | * email:335891510@qq.com 11 | */ 12 | public class LogUtils { 13 | 14 | private final static boolean isLog = true; 15 | 16 | public static void debugInfo(String tag, String msg) { 17 | if (!isLog || TextUtils.isEmpty(msg)) return; 18 | Log.d(tag, msg); 19 | 20 | } 21 | 22 | /** 23 | * author hhj 24 | * TODO 25 | * 26 | * @param msg void 27 | */ 28 | public static void debugInfo(String msg) { 29 | debugInfo("zhibo", msg); 30 | } 31 | 32 | public static void warnInfo(String tag, String msg) { 33 | if (!isLog || TextUtils.isEmpty(msg)) return; 34 | Log.w(tag, msg); 35 | 36 | } 37 | 38 | /** 39 | * author hhj 40 | * TODO 41 | * 42 | * @param msg void 43 | */ 44 | public static void warnInfo(String msg) { 45 | warnInfo("zhibo", msg); 46 | } 47 | 48 | /** 49 | * author hhj 50 | * TODO 使用Log来显示调试信息,因为log在实现上每个message有4k字符长度限制 51 | * 所以这里使用自己分节的方式来输出足够长度的message 52 | * 53 | * @param tag 54 | * @param str void 55 | */ 56 | 57 | public static void debugLongInfo(String tag, String str) { 58 | if (!isLog) return; 59 | str = str.trim(); 60 | int index = 0; 61 | int maxLength = 3500; 62 | String sub; 63 | while (index < str.length()) { 64 | // java的字符不允许指定超过总的长度end 65 | if (str.length() <= index + maxLength) { 66 | sub = str.substring(index); 67 | } else { 68 | sub = str.substring(index, index + maxLength); 69 | } 70 | 71 | index += maxLength; 72 | Log.d(tag, sub.trim()); 73 | } 74 | } 75 | 76 | /** 77 | * author hhj 78 | * TODO 79 | * 80 | * @param str void 81 | */ 82 | public static void debugLongInfo(String str) { 83 | debugLongInfo("zhibo", str); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/java/me/jessyan/audiomanager/util/UiUitls.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager.util; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | /** 7 | * Created by jess on 7/26/16. 8 | */ 9 | public class UiUitls { 10 | public static Toast mToast; 11 | /** 12 | * 单列toast 13 | * 14 | * @param string 15 | */ 16 | 17 | public static void makeText(Context context, String string) { 18 | if (mToast == null) { 19 | mToast = Toast.makeText(context, string, Toast.LENGTH_SHORT); 20 | } 21 | mToast.setText(string); 22 | mToast.show(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /audiomanagersdk/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AudioManagerSdk 3 | 4 | -------------------------------------------------------------------------------- /audiomanagersdk/src/test/java/me/jessyan/audiomanager/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package me.jessyan.audiomanager; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /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.0.0' 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 | 21 | task clean(type: Delete) { 22 | delete rootProject.buildDir 23 | } 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Tue Aug 30 12:20:14 CST 2016 16 | systemProp.http.proxyHost=127.0.0.1 17 | systemProp.http.proxyPort=1080 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JessYanCoding/AudioManagerSDK/cf872a8513aaf3367f03cf86c594dab0a356e619/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 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.10-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 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':audiomanagersdk' 2 | --------------------------------------------------------------------------------