├── app ├── .gitignore ├── libs │ ├── galaxy-v2.0.jar │ ├── com.baidu.tts.jar │ ├── arm64-v8a │ │ ├── libbdtts.so │ │ ├── libbd_etts.so │ │ ├── libgnustl_shared.so │ │ ├── libBDSpeechDecoder_V1.so │ │ ├── libbdEASRAndroid.v1.9.11.so │ │ ├── libBDVoiceRecognitionClient_MFE_V1_s2.so │ │ └── libbd_easr_s1_merge_normal_20151216.dat.so │ ├── armeabi │ │ ├── libbd_etts.so │ │ ├── libbdtts.so │ │ ├── libgnustl_shared.so │ │ ├── libBDSpeechDecoder_V1.so │ │ ├── libbdEASRAndroid.v1.9.11.so │ │ ├── libBDVoiceRecognitionClient_MFE_V1_s2.so │ │ └── libbd_easr_s1_merge_normal_20151216.dat.so │ └── VoiceRecognition-2.1.20.jar ├── src │ ├── main │ │ ├── assets │ │ │ ├── WakeUp.bin │ │ │ ├── bd_etts_text.dat │ │ │ └── bd_etts_speech_female.dat │ │ ├── res │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── mic_btn.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── input_btn.png │ │ │ │ ├── mic_btn_pressed.png │ │ │ │ ├── input_btn_disabled.png │ │ │ │ └── input_btn_pressed.png │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── input_btn.png │ │ │ │ ├── ic_launcher.png │ │ │ │ ├── input_btn_disabled.png │ │ │ │ └── input_btn_pressed.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── drawable │ │ │ │ └── redius_rec.xml │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── fragment_voice_input.xml │ │ │ │ └── fragment_main.xml │ │ │ └── values-zh │ │ │ │ └── strings.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── ooolab │ │ │ │ └── whatiswhat │ │ │ │ ├── QueryParser.java │ │ │ │ ├── HurlStackWrapper.java │ │ │ │ ├── StringRequestWrapper.java │ │ │ │ ├── Utils.java │ │ │ │ ├── WebViewClientWrapper.java │ │ │ │ ├── BaiduBaikeHandler.java │ │ │ │ ├── MainFragment.java │ │ │ │ ├── MicrophoneView.java │ │ │ │ ├── VoiceInputFragment.java │ │ │ │ └── MainActivity.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── ooolab │ │ │ └── whatiswhat │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── ooolab │ │ └── whatiswhat │ │ └── ExampleInstrumentedTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat ├── gradlew └── LICENSE /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app/libs/galaxy-v2.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/galaxy-v2.0.jar -------------------------------------------------------------------------------- /app/libs/com.baidu.tts.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/com.baidu.tts.jar -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libbdtts.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/arm64-v8a/libbdtts.so -------------------------------------------------------------------------------- /app/libs/armeabi/libbd_etts.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/armeabi/libbd_etts.so -------------------------------------------------------------------------------- /app/libs/armeabi/libbdtts.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/armeabi/libbdtts.so -------------------------------------------------------------------------------- /app/src/main/assets/WakeUp.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/assets/WakeUp.bin -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libbd_etts.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/arm64-v8a/libbd_etts.so -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/libs/VoiceRecognition-2.1.20.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/VoiceRecognition-2.1.20.jar -------------------------------------------------------------------------------- /app/libs/armeabi/libgnustl_shared.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/armeabi/libgnustl_shared.so -------------------------------------------------------------------------------- /app/src/main/assets/bd_etts_text.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/assets/bd_etts_text.dat -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libgnustl_shared.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/arm64-v8a/libgnustl_shared.so -------------------------------------------------------------------------------- /app/libs/armeabi/libBDSpeechDecoder_V1.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/armeabi/libBDSpeechDecoder_V1.so -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/mic_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xhdpi/mic_btn.png -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libBDSpeechDecoder_V1.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/arm64-v8a/libBDSpeechDecoder_V1.so -------------------------------------------------------------------------------- /app/libs/armeabi/libbdEASRAndroid.v1.9.11.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/armeabi/libbdEASRAndroid.v1.9.11.so -------------------------------------------------------------------------------- /app/src/main/assets/bd_etts_speech_female.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/assets/bd_etts_speech_female.dat -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/input_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xhdpi/input_btn.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/input_btn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xxxhdpi/input_btn.png -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libbdEASRAndroid.v1.9.11.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/arm64-v8a/libbdEASRAndroid.v1.9.11.so -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/mic_btn_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xhdpi/mic_btn_pressed.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/input_btn_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xhdpi/input_btn_disabled.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/input_btn_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xhdpi/input_btn_pressed.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/input_btn_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xxxhdpi/input_btn_disabled.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/input_btn_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/src/main/res/mipmap-xxxhdpi/input_btn_pressed.png -------------------------------------------------------------------------------- /app/libs/armeabi/libBDVoiceRecognitionClient_MFE_V1_s2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/armeabi/libBDVoiceRecognitionClient_MFE_V1_s2.so -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libBDVoiceRecognitionClient_MFE_V1_s2.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/arm64-v8a/libBDVoiceRecognitionClient_MFE_V1_s2.so -------------------------------------------------------------------------------- /app/libs/armeabi/libbd_easr_s1_merge_normal_20151216.dat.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/armeabi/libbd_easr_s1_merge_normal_20151216.dat.so -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libbd_easr_s1_merge_normal_20151216.dat.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/solrex/WhatIsWhat/HEAD/app/libs/arm64-v8a/libbd_easr_s1_merge_normal_20151216.dat.so -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | app/src/main/res/values/apikey.xml 11 | -------------------------------------------------------------------------------- /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.14.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #EF5350 4 | #F44336 5 | #FF4081 6 | #FAFAFA 7 | #212121 8 | #FFFFFF 9 | #9E9E9E 10 | 11 | -------------------------------------------------------------------------------- /app/src/test/java/com/ooolab/whatiswhat/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/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/solrex/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/main/res/drawable/redius_rec.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 17 | 18 | 19 | 24 | 25 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/QueryParser.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import android.util.Log; 4 | 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | /** 9 | * Created by solrex on 2016/12/21. 10 | */ 11 | 12 | public class QueryParser { 13 | private static String TAG = "QueryParser"; 14 | 15 | public static String getWhat(String str) { 16 | String what = ""; 17 | Pattern whatPattern; 18 | if (str.contains("什么是")) { 19 | whatPattern = Pattern.compile("什么是(.+)"); 20 | } else { 21 | whatPattern = Pattern.compile("(.+)是什么"); 22 | } 23 | Matcher match = whatPattern.matcher(str); 24 | if (match.find()) { 25 | what = match.group(1); 26 | } 27 | Log.i(TAG, "getWhat(" + str + ")=" + what); 28 | return what; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/ooolab/whatiswhat/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.ooolab.whatiswhat", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | What is what 3 | Voice input button 4 | 你可以说:什么是大熊猫?孔雀是什么? 5 | 很抱歉,没有找到关于"%1$s"的视频,你可以试试其它问题。 6 | 很抱歉,访问网页出错了,检查是不是网络连接出问题了? 7 | 很抱歉,我还不支持"%1$s"这个问题。 8 | 语音识别出错,因为"%1$s" 9 | 出现了网络请求超时,请检查网络。 10 | 出现了未知网络错误,请检查网络。 11 | 出现了录音错误,请检查录音权限和麦克风。 12 | 服务器端出错,请稍后重试。 13 | 出现了客户端调用错误,请稍后重试。 14 | 没有听清,请靠近话筒重试。 15 | 无法识别语音,请靠近话筒重试。 16 | 服务器繁忙,请稍后重试。 17 | 出现了权限错误,请检查APP权限。 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 什么是什么 4 | 点击按钮语音输入 5 | 你可以说:什么是大熊猫?孔雀是什么? 6 | 很抱歉,没有找到关于“%1$s”的视频,你可以试试其它问题。 7 | 很抱歉,访问网页出错了,检查是不是网络连接出问题了? 8 | 很抱歉,我还不支持“%1$s”这个问题。 9 | 语音识别失败,%1$s 10 | 出现了网络请求超时,请检查网络。 11 | 出现了未知网络错误,请检查网络。 12 | 出现了录音错误,请检查录音权限和麦克风。 13 | 服务器端出错,请稍后重试。 14 | 出现了客户端调用错误,请稍后重试。 15 | 没有听清,请靠近话筒重试。 16 | 无法识别语音,请靠近话筒重试。 17 | 服务器繁忙,请稍后重试。 18 | 出现了权限错误,请检查APP权限。 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_voice_input.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 16 | 23 | 24 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "25.0.0" 6 | defaultConfig { 7 | applicationId "com.ooolab.whatiswhat" 8 | minSdkVersion 16 9 | targetSdkVersion 25 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | sourceSets { 21 | main { 22 | jniLibs.srcDirs = ['libs'] 23 | } 24 | } 25 | } 26 | 27 | dependencies { 28 | compile fileTree(include: ['*.jar'], dir: 'libs') 29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 30 | exclude group: 'com.android.support', module: 'support-annotations' 31 | }) 32 | compile files('libs/VoiceRecognition-2.1.20.jar') 33 | compile files('libs/com.baidu.tts.jar') 34 | compile 'com.android.support:appcompat-v7:25.0.1' 35 | compile 'com.android.support:support-v4:24.2.1' 36 | compile 'com.android.volley:volley:1.0.0' 37 | testCompile 'junit:junit:4.12' 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/HurlStackWrapper.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import android.util.Log; 4 | 5 | import com.android.volley.toolbox.HurlStack; 6 | 7 | import java.io.IOException; 8 | import java.net.HttpURLConnection; 9 | import java.net.Proxy; 10 | import java.net.ProxySelector; 11 | import java.net.URL; 12 | 13 | 14 | /** 15 | * Created by solrex on 2016/12/21. 16 | */ 17 | 18 | public class HurlStackWrapper extends HurlStack { 19 | private static String TAG = "HurlStackWrapper"; 20 | 21 | /** 22 | * Create an {@link HttpURLConnection} for the specified {@code url}. 23 | * 支持使用系统代理,支持跟随 302 重定向 24 | */ 25 | @Override 26 | protected HttpURLConnection createConnection(URL url) throws IOException { 27 | Log.i(TAG, "createConnection: " + url); 28 | 29 | final HttpURLConnection urlConnection; 30 | Proxy proxy = null; 31 | try { 32 | proxy = ProxySelector.getDefault().select(url.toURI()).get(0); 33 | } catch (Exception e) { 34 | e.printStackTrace(); 35 | } 36 | if (proxy == null) { 37 | urlConnection = (HttpURLConnection) url.openConnection(); 38 | } else { 39 | urlConnection = (HttpURLConnection) url.openConnection(proxy); 40 | } 41 | urlConnection.setInstanceFollowRedirects(true); 42 | return urlConnection; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/StringRequestWrapper.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import com.android.volley.NetworkResponse; 4 | import com.android.volley.Request; 5 | import com.android.volley.Response; 6 | import com.android.volley.toolbox.HttpHeaderParser; 7 | import com.android.volley.toolbox.StringRequest; 8 | 9 | import java.io.UnsupportedEncodingException; 10 | import java.nio.charset.StandardCharsets; 11 | 12 | import static java.net.Proxy.Type.HTTP; 13 | 14 | /** 15 | * Created by solrex on 2016/12/21. 16 | */ 17 | 18 | public class StringRequestWrapper extends StringRequest { 19 | /** 20 | * Creates a new GET request. 21 | * 22 | * @param url URL to fetch the string at 23 | * @param listener Listener to receive the String response 24 | * @param errorListener Error listener, or null to ignore errors 25 | */ 26 | public StringRequestWrapper(String url, Response.Listener listener, Response.ErrorListener errorListener) { 27 | super(url, listener, errorListener); 28 | } 29 | 30 | @Override 31 | protected Response parseNetworkResponse(NetworkResponse response) { 32 | String parsed; 33 | try { 34 | parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers, "UTF-8")); 35 | } catch (UnsupportedEncodingException e) { 36 | parsed = new String(response.data); 37 | } 38 | return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/Utils.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import android.content.res.AssetManager; 4 | import android.util.Log; 5 | 6 | import java.io.File; 7 | import java.io.FileNotFoundException; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | 12 | /** 13 | * Created by solrex on 2016/12/22. 14 | */ 15 | 16 | public class Utils { 17 | public static String TAG = "Utils"; 18 | 19 | public static void copyAssetsToSdcard(AssetManager assets,String assetName, File dstFile) { 20 | copyAssetsToSdcard(assets, assetName, dstFile, false); 21 | } 22 | 23 | public static void copyAssetsToSdcard(AssetManager assets,String assetName, File dstFile, boolean overwrite) { 24 | InputStream is = null; 25 | FileOutputStream out = null; 26 | if (dstFile.exists() && (!overwrite)) { 27 | return; 28 | } 29 | try { 30 | is = assets.open(assetName); 31 | out = new FileOutputStream(dstFile); 32 | byte[] buffer = new byte[1024]; 33 | int size = 0; 34 | while ((size = is.read(buffer, 0, 1024)) >= 0) { 35 | out.write(buffer, 0, size); 36 | } 37 | } catch (FileNotFoundException e) { 38 | Log.w(TAG, "copyAssetsToSdcard: ", e); 39 | e.printStackTrace(); 40 | } catch (IOException e) { 41 | Log.w(TAG, "copyAssetsToSdcard: ", e); 42 | } finally { 43 | if (out != null) { 44 | try { 45 | out.close(); 46 | } catch (IOException e) { 47 | Log.w(TAG, "copyAssetsToSdcard: ", e); 48 | } 49 | } 50 | try { 51 | if (is != null) { 52 | is.close(); 53 | } 54 | } catch (IOException e) { 55 | Log.w(TAG, "copyAssetsToSdcard: ", e); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WhatIsWhat: 一个全语音交互的知识探索APP 2 | 3 | ## 背景 4 | 5 | 近两年都是在做人工智能的技术产品,也一直在思考 AI 能给人们带来哪些便利。某天闲聊的时候,有个妈妈同事说,她家宝宝问她很多东西不懂,只好去搜索,发现百度百科的不少词条有个“秒懂百科”,用视频讲解百科词条,宝宝很爱看。只是可惜宝宝不认字,不会自己搜索。然后我就想,要是有个工具,能用语音问问题,语音或者视频回答问题,那挺不错啊,就有了这个 APP。 6 | 7 | 把它开源出来,是有一个想法:其实每个爸爸妈妈,都希望自己给孩子的爱是特殊的呀,那 TA 就可以在这个 APP 基础上进行修改,比如在每条语音提示前都加上自己宝宝的名字,甚至直接替换成自己的录音。那这就变成了送给宝宝的一份用心的独一无二的礼物了! 8 | 9 | ## 我想先体验一下 10 | 11 | 你可以点击链接 https://github.com/solrex/WhatIsWhat/releases 或者扫描下方二维码访问下载列表,下载一个编译好的 `.apk` 文件,安装在安卓手机上体验一下。 12 | 13 | ![下载页面](http://chart.apis.google.com/chart?cht=qr&chld=|0&choe=UTF-8&chs=200x200&chl=https%3A%2F%2Fgithub.com%2Fsolrex%2FWhatIsWhat%2Freleases) 14 | 15 | 下面是一段功能演示视频,可以点击下图到爱奇艺查看: 16 | 17 | [![演示视频,点击查看](http://u3.qiyipic.com/image/20161223/fb/81/uv_4015415252_m_601_180_101.jpg)](http://www.iqiyi.com/w_19ru8huel1.html) 18 | 19 | ## 怎么编译 20 | 21 | ### 下载代码,配置编译环境 22 | 23 | 通过 git clone,或者下载 zip 包的方式,将源代码下载到本地。 24 | ``` 25 | git clone https://github.com/solrex/WhatIsWhat.git 26 | OR 27 | git clone git@github.com:solrex/WhatIsWhat.git 28 | ``` 29 | 安装 Android Studio,用 Import Project 将源代码目录导入到 Android Studio 中。如本地 SDK Manager 没有下载所有版本的 SDK,也许需要调整一下 app 的 build.gradle,主要是将 `compileSdkVersion, buildToolsVersion, minSdkVersion, targetSdkVersion` 这几个依赖版本,调整到本地 SDK Manager 已经有的版本。 30 | 31 | ### 申请百度语音识别调用接口(免费) 32 | 33 | 到 http://yuyin.baidu.com/ ,使用百度账号登陆,注册成为开发者。开发者审核通过后,新建一个应用。新建应用时,勾选使用“语音识别”、“语音合成”两大功能,应用包名填入“`com.ooolab.whatiswhat`”。 34 | 35 | 点击应用右侧的“查看key”链接,将显示的信息填入以下 xml 文件: 36 | 37 | ``` xml 38 | 39 | 40 | YOUR_APP_ID 41 | YOUR_API_KEY 42 | YOUR_SECRET_KEY 43 | 44 | ``` 45 | 将文件保存到 `WhatIsWhat/app/src/main/res/values/apikey.xml`,这是为您的应用添加语音识别和语音合成的调用授权。 46 | 47 | 然后就可以按照一般的 Android 项目,进行编译了。 48 | 49 | ## 遇到 BUG 了怎么办? 50 | 51 | 因为这还是一个非常不成熟的 APP,甚至可以说它还只是个 demo 阶段,所以遇到 BUG 的概率应该还是很大的。 52 | 53 | 当你遇到问题时,欢迎在 Issues: https://github.com/solrex/WhatIsWhat/issues 报告问题,将你的运行时环境(例如:手机型号,操作系统版本,联网情况)、复现问题的操作顺序(例如:先点击了什么,后点击了什么)、logcat 捕获的异常(如果有的话),填写到 Issue 中,以便定位和修复问题。 54 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/WebViewClientWrapper.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import android.webkit.WebResourceRequest; 4 | import android.webkit.WebView; 5 | import android.webkit.WebViewClient; 6 | 7 | /** 8 | * Created by solrex on 2016/12/19. 9 | */ 10 | 11 | public class WebViewClientWrapper extends WebViewClient { 12 | /** 13 | * Give the host application a chance to take over the control when a new 14 | * url is about to be loaded in the current WebView. If WebViewClient is not 15 | * provided, by default WebView will ask Activity Manager to choose the 16 | * proper handler for the url. If WebViewClient is provided, return true 17 | * means the host application handles the url, while return false means the 18 | * current WebView handles the url. 19 | *

20 | *

Notes: 21 | *

28 | *

29 | * 30 | * @param view The WebView that is initiating the callback. 31 | * @param request Object containing the details of the request. 32 | * @return True if the host application wants to leave the current WebView 33 | * and handle the url itself, otherwise return false. 34 | */ 35 | @Override 36 | public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { 37 | view.loadUrl(request.toString()); 38 | return true; 39 | } 40 | 41 | /** 42 | * Notify the host application that a page has finished loading. This method 43 | * is called only for main frame. When onPageFinished() is called, the 44 | * rendering picture may not be updated yet. To get the notification for the 45 | * new Picture, use {@link WebView.PictureListener#onNewPicture}. 46 | * 47 | * @param view The WebView that is initiating the callback. 48 | * @param url The url of the page. 49 | */ 50 | @Override 51 | public void onPageFinished(WebView view, String url) { 52 | super.onPageFinished(view, url); 53 | if (url.contains("wikisecond")) { 54 | view.loadUrl("javascript:(function(){document.getElementsByClassName(\"video-container\")[0].click();})()"); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/BaiduBaikeHandler.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import android.util.Log; 4 | import android.view.View; 5 | import android.webkit.WebView; 6 | import android.widget.TextView; 7 | 8 | import com.android.volley.Response; 9 | import com.android.volley.VolleyError; 10 | 11 | import java.io.UnsupportedEncodingException; 12 | import java.net.URLEncoder; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | /** 17 | * Created by solrex on 2016/12/20. 18 | */ 19 | 20 | public class BaiduBaikeHandler implements Response.Listener, Response.ErrorListener { 21 | private static String TAG = "BaiduBaikeHandler"; 22 | public static String site = "https://wapbaike.baidu.com"; 23 | 24 | private MainActivity mActivity; 25 | private WebView mWebView; 26 | private String mWhat; 27 | 28 | public BaiduBaikeHandler(MainActivity activity) { 29 | mActivity = activity; 30 | mWebView = (WebView) mActivity.findViewById(R.id.webview); 31 | } 32 | 33 | public void search(String what) { 34 | mWhat = what; 35 | try { 36 | String url = BaiduBaikeHandler.site + "/item/" + URLEncoder.encode(mWhat, "UTF-8"); 37 | StringRequestWrapper urlReq = new StringRequestWrapper(url, this, this); 38 | mActivity.getVolleyQueue().add(urlReq); 39 | } catch (UnsupportedEncodingException e) { 40 | Log.w(TAG, "search: ", e); 41 | } 42 | } 43 | 44 | @Override 45 | public void onResponse(String response) { 46 | Log.i(TAG, "onResponse: " + response.substring(0, 32)); 47 | //Log.w("Baike", "Response: " + response); 48 | Pattern secondSummary = 49 | Pattern.compile("
"); 50 | Matcher match = secondSummary.matcher(response); 51 | if (match.find()) { 52 | String secondPath = match.group(1); 53 | mActivity.hideInfo(); 54 | mWebView.loadUrl(this.site + secondPath); 55 | if (mWebView.getVisibility() != View.VISIBLE) { 56 | mWebView.setVisibility(View.VISIBLE); 57 | } 58 | } else { 59 | mActivity.showInfo(String.format( 60 | mActivity.getResources().getString(R.string.no_miaodong_result), 61 | mWhat)); 62 | } 63 | } 64 | 65 | @Override 66 | public void onErrorResponse(VolleyError error) { 67 | Log.i(TAG, "onErrorResponse: ", error); 68 | mActivity.showInfo(R.string.error_on_fetch_url); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 18 | 19 | 30 | 31 | 43 | 44 | 45 | 60 | 61 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/MainFragment.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import android.content.Context; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | 11 | import com.ooolab.whatiswhat.R; 12 | 13 | /** 14 | * A simple {@link Fragment} subclass. 15 | * Activities that contain this fragment must implement the 16 | * {@link MainFragment.OnFragmentInteractionListener} interface 17 | * to handle interaction events. 18 | * Use the {@link MainFragment#newInstance} factory method to 19 | * create an instance of this fragment. 20 | */ 21 | public class MainFragment extends Fragment { 22 | // TODO: Rename parameter arguments, choose names that match 23 | // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER 24 | private static final String ARG_PARAM1 = "param1"; 25 | private static final String ARG_PARAM2 = "param2"; 26 | 27 | // TODO: Rename and change types of parameters 28 | private String mParam1; 29 | private String mParam2; 30 | 31 | private OnInputBtnClickListner mListener; 32 | 33 | public MainFragment() { 34 | // Required empty public constructor 35 | } 36 | 37 | /** 38 | * Use this factory method to create a new instance of 39 | * this fragment using the provided parameters. 40 | * 41 | * @param param1 Parameter 1. 42 | * @param param2 Parameter 2. 43 | * @return A new instance of fragment MainFragment. 44 | */ 45 | // TODO: Rename and change types and number of parameters 46 | public static MainFragment newInstance(String param1, String param2) { 47 | MainFragment fragment = new MainFragment(); 48 | Bundle args = new Bundle(); 49 | args.putString(ARG_PARAM1, param1); 50 | args.putString(ARG_PARAM2, param2); 51 | fragment.setArguments(args); 52 | return fragment; 53 | } 54 | 55 | @Override 56 | public void onCreate(Bundle savedInstanceState) { 57 | super.onCreate(savedInstanceState); 58 | if (getArguments() != null) { 59 | mParam1 = getArguments().getString(ARG_PARAM1); 60 | mParam2 = getArguments().getString(ARG_PARAM2); 61 | } 62 | } 63 | 64 | @Override 65 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 66 | Bundle savedInstanceState) { 67 | // Inflate the layout for this fragment 68 | return inflater.inflate(R.layout.fragment_main, container, false); 69 | } 70 | 71 | // TODO: Rename method, update argument and hook method into UI event 72 | public void onButtonPressed(View v) { 73 | if (mListener != null) { 74 | mListener.onInputBtnClick(v); 75 | } 76 | } 77 | 78 | @Override 79 | public void onAttach(Context context) { 80 | super.onAttach(context); 81 | if (context instanceof OnInputBtnClickListner) { 82 | mListener = (OnInputBtnClickListner) context; 83 | } else { 84 | throw new RuntimeException(context.toString() 85 | + " must implement OnFragmentInteractionListener"); 86 | } 87 | } 88 | 89 | @Override 90 | public void onDetach() { 91 | super.onDetach(); 92 | mListener = null; 93 | } 94 | 95 | /** 96 | * This interface must be implemented by activities that contain this 97 | * fragment to allow an interaction in this fragment to be communicated 98 | * to the activity and potentially other fragments contained in that 99 | * activity. 100 | *

101 | * See the Android Training lesson Communicating with Other Fragments for more information. 104 | */ 105 | public interface OnInputBtnClickListner { 106 | // TODO: Update argument type and name 107 | void onInputBtnClick(View v); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/MicrophoneView.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import android.animation.AnimatorSet; 4 | import android.animation.ObjectAnimator; 5 | import android.content.Context; 6 | import android.graphics.Bitmap; 7 | import android.graphics.BitmapFactory; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Paint; 11 | import android.speech.SpeechRecognizer; 12 | import android.util.AttributeSet; 13 | import android.util.Log; 14 | import android.util.TypedValue; 15 | import android.view.MotionEvent; 16 | import android.view.View; 17 | 18 | 19 | /** 20 | * TODO: document your custom view class. 21 | */ 22 | public class MicrophoneView extends View { 23 | 24 | private static final int STATE_RECORDING = 0; 25 | private static final int STATE_PRESSED = 1; 26 | private int mState = STATE_RECORDING; 27 | 28 | private Bitmap mRecordingBitmap; 29 | private Bitmap mPressedBitmap; 30 | 31 | private Paint mPaint; 32 | private AnimatorSet mAnimatorSet = new AnimatorSet(); 33 | 34 | private float mMinRadius; 35 | private float mMaxRadius; 36 | private float mCurrentRadius; 37 | 38 | public MicrophoneView(Context context) { 39 | super(context); 40 | init(null, 0); 41 | } 42 | 43 | public MicrophoneView(Context context, AttributeSet attrs) { 44 | super(context, attrs); 45 | init(attrs, 0); 46 | } 47 | 48 | public MicrophoneView(Context context, AttributeSet attrs, int defStyle) { 49 | super(context, attrs, defStyle); 50 | init(attrs, defStyle); 51 | } 52 | 53 | private void init(AttributeSet attrs, int defStyle) { 54 | mRecordingBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.mic_btn); 55 | mPressedBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.mic_btn_pressed); 56 | 57 | mPaint = new Paint(); 58 | mPaint.setAntiAlias(true); 59 | mPaint.setColor(Color.argb(255, 0x9e, 0x9e, 0x9e)); 60 | 61 | mMinRadius = dp2px(getContext(), 96) / 2; 62 | mCurrentRadius = mMinRadius; 63 | 64 | setClickable(true); 65 | } 66 | 67 | public static int dp2px(Context context, int dp){ 68 | return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, context.getResources().getDisplayMetrics()); 69 | } 70 | 71 | @Override 72 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 73 | super.onSizeChanged(w, h, oldw, oldh); 74 | mMaxRadius = Math.min(w, h) / 2; 75 | Log.d("", "MaxRadius: " + mMaxRadius); 76 | } 77 | 78 | @Override 79 | protected void onDraw(Canvas canvas) { 80 | super.onDraw(canvas); 81 | 82 | int width = canvas.getWidth(); 83 | int height = canvas.getHeight(); 84 | 85 | if(mCurrentRadius > mMinRadius){ 86 | canvas.drawCircle(width / 2, height / 2, mCurrentRadius, mPaint); 87 | } 88 | 89 | switch (mState){ 90 | case STATE_RECORDING: 91 | canvas.drawBitmap(mRecordingBitmap, width / 2 - mMinRadius, height / 2 - mMinRadius, mPaint); 92 | break; 93 | case STATE_PRESSED: 94 | canvas.drawBitmap(mPressedBitmap, width / 2 - mMinRadius, height / 2 - mMinRadius, mPaint); 95 | break; 96 | } 97 | } 98 | 99 | public void animateRadius(float radius){ 100 | if(radius <= mCurrentRadius){ 101 | return; 102 | } 103 | if(radius > mMaxRadius){ 104 | radius = mMaxRadius; 105 | }else if(radius < mMinRadius){ 106 | radius = mMinRadius; 107 | } 108 | if(radius == mCurrentRadius){ 109 | return; 110 | } 111 | if(mAnimatorSet.isRunning()){ 112 | mAnimatorSet.cancel(); 113 | } 114 | mAnimatorSet.playSequentially( 115 | ObjectAnimator.ofFloat(this, "CurrentRadius", getCurrentRadius(), radius).setDuration(50), 116 | ObjectAnimator.ofFloat(this, "CurrentRadius", radius, mMinRadius).setDuration(600) 117 | ); 118 | mAnimatorSet.start(); 119 | } 120 | 121 | public float getCurrentRadius() { 122 | return mCurrentRadius; 123 | } 124 | 125 | public void setCurrentRadius(float currentRadius) { 126 | mCurrentRadius = currentRadius; 127 | invalidate(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/VoiceInputFragment.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | import android.content.ComponentName; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.speech.RecognitionListener; 8 | import android.speech.SpeechRecognizer; 9 | import android.support.v4.app.Fragment; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | import android.widget.TextView; 14 | 15 | import com.baidu.speech.VoiceRecognitionService; 16 | 17 | import java.util.ArrayList; 18 | 19 | /*enum VSRErrors { 20 | NETWORK_TIMEOUT(1, "error_on_network_timeout"), 21 | NETWORK_UNKNOWN(2, "error_on_network_unknown"), 22 | AUDIO_RECORDING(3, "error_on_audio_recording"), 23 | SERVER_RESPONSE(4, "error_on_server_response"), 24 | CLIENT_UNKNOWN(5, "error_on_client_unknown"), 25 | SPEECH_TIMEOUT(6, "error_on_speech_timeout"), 26 | NO_RESULT(7, "error_on_no_result"), 27 | RECOGNIZER_BUSY(8, "error_on_recognizer_busy"), 28 | INSUFFICIENT_PERMISSIONS(9, "error_on_insufficient_permissions"); 29 | 30 | private final int code; 31 | private final String message; 32 | 33 | private VSRErrors(int code, String message) { 34 | this.code = code; 35 | this.message = message; 36 | } 37 | 38 | public String getMessage() { 39 | return message; 40 | } 41 | 42 | public int getCode() { 43 | return code; 44 | } 45 | 46 | @Override 47 | public String toString() { 48 | return code + ": " + message; 49 | } 50 | }*/ 51 | 52 | /** 53 | * A simple {@link Fragment} subclass. 54 | * Activities that contain this fragment must implement the 55 | * {@link VoiceInputFragment.OnFragmentInteractionListener} interface 56 | * to handle interaction events. 57 | * Use the {@link VoiceInputFragment#newInstance} factory method to 58 | * create an instance of this fragment. 59 | */ 60 | public class VoiceInputFragment extends Fragment implements RecognitionListener { 61 | private SpeechRecognizer mSpeechRecognizer; 62 | private TextView mRecogTextView; 63 | private MicrophoneView mMicButtonView; 64 | 65 | private OnRecogResultListener mListener; 66 | 67 | public VoiceInputFragment() { 68 | // Required empty public constructor 69 | } 70 | 71 | /** 72 | * Use this factory method to create a new instance of 73 | * this fragment using the provided parameters. 74 | * 75 | * @param param1 Parameter 1. 76 | * @param param2 Parameter 2. 77 | * @return A new instance of fragment VoiceInputFragment. 78 | */ 79 | // TODO: Rename and change types and number of parameters 80 | // public static VoiceInputFragment newInstance(String param1, String param2) { 81 | // VoiceInputFragment fragment = new VoiceInputFragment(); 82 | // Bundle args = new Bundle(); 83 | // args.putString(ARG_PARAM1, param1); 84 | // args.putString(ARG_PARAM2, param2); 85 | // fragment.setArguments(args); 86 | // return fragment; 87 | // } 88 | 89 | @Override 90 | public void onCreate(Bundle savedInstanceState) { 91 | super.onCreate(savedInstanceState); 92 | mSpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(getContext(), new ComponentName(getContext(), VoiceRecognitionService.class)); 93 | mSpeechRecognizer.setRecognitionListener(this); 94 | } 95 | 96 | private void startRecognition() { 97 | Intent intent = new Intent(); 98 | mSpeechRecognizer.startListening(intent); 99 | mRecogTextView.setText(R.string.voice_input_intro); 100 | } 101 | 102 | 103 | public void stopRecognition() { 104 | mSpeechRecognizer.stopListening(); 105 | } 106 | 107 | @Override 108 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 109 | Bundle savedInstanceState) { 110 | // Inflate the layout for this fragment 111 | View v = inflater.inflate(R.layout.fragment_voice_input, container, false); 112 | mRecogTextView = (TextView) v.findViewById(R.id.voice_input_partial); 113 | mMicButtonView = (MicrophoneView) v.findViewById(R.id.mic_btn); 114 | mMicButtonView.setOnClickListener(new View.OnClickListener() { 115 | public void onClick(View v) { 116 | stopRecognition(); 117 | } 118 | }); 119 | startRecognition(); 120 | return v; 121 | } 122 | 123 | @Override 124 | public void onAttach(Context context) { 125 | super.onAttach(context); 126 | if (context instanceof OnRecogResultListener) { 127 | mListener = (OnRecogResultListener) context; 128 | } else { 129 | throw new RuntimeException(context.toString() 130 | + " must implement OnFragmentInteractionListener"); 131 | } 132 | } 133 | 134 | @Override 135 | public void onDetach() { 136 | super.onDetach(); 137 | mListener = null; 138 | mSpeechRecognizer.destroy(); 139 | } 140 | 141 | 142 | /** 143 | * This interface must be implemented by activities that contain this 144 | * fragment to allow an interaction in this fragment to be communicated 145 | * to the activity and potentially other fragments contained in that 146 | * activity. 147 | *

148 | * See the Android Training lesson Communicating with Other Fragments for more information. 151 | */ 152 | public interface OnRecogResultListener { 153 | // TODO: Update argument type and name 154 | void onRecogResult(Bundle bundle); 155 | } 156 | 157 | @Override 158 | public void onReadyForSpeech(Bundle bundle) { 159 | 160 | } 161 | 162 | @Override 163 | public void onBeginningOfSpeech() { 164 | 165 | } 166 | 167 | @Override 168 | public void onRmsChanged(float v) { 169 | mMicButtonView.animateRadius(v); 170 | } 171 | 172 | @Override 173 | public void onBufferReceived(byte[] bytes) { 174 | 175 | } 176 | 177 | @Override 178 | public void onEndOfSpeech() { 179 | 180 | } 181 | 182 | @Override 183 | public void onError(int i) { 184 | Bundle bundle = new Bundle(); 185 | bundle.putInt("error", i); 186 | if (mListener != null) { 187 | mListener.onRecogResult(bundle); 188 | } 189 | } 190 | 191 | @Override 192 | public void onResults(Bundle bundle) { 193 | if (mListener != null) { 194 | mListener.onRecogResult(bundle); 195 | } 196 | } 197 | 198 | @Override 199 | public void onPartialResults(Bundle bundle) { 200 | ArrayList partialResults = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); 201 | if (partialResults.size() > 0) { 202 | mRecogTextView.setText(partialResults.get(0)); 203 | } 204 | } 205 | 206 | @Override 207 | public void onEvent(int eventType, Bundle bundle) { 208 | } 209 | 210 | } 211 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/java/com/ooolab/whatiswhat/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ooolab.whatiswhat; 2 | 3 | 4 | import android.content.pm.ApplicationInfo; 5 | import android.content.pm.PackageManager; 6 | import android.os.Bundle; 7 | import android.speech.SpeechRecognizer; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.text.Editable; 10 | import android.text.TextWatcher; 11 | import android.util.AndroidRuntimeException; 12 | import android.util.Log; 13 | import android.view.View; 14 | import android.webkit.WebSettings; 15 | import android.webkit.WebView; 16 | import android.widget.TextView; 17 | 18 | import com.android.volley.RequestQueue; 19 | import com.android.volley.toolbox.Volley; 20 | import com.baidu.speech.EventListener; 21 | import com.baidu.speech.EventManager; 22 | import com.baidu.speech.EventManagerFactory; 23 | import com.baidu.tts.client.SpeechError; 24 | import com.baidu.tts.client.SpeechSynthesizer; 25 | import com.baidu.tts.client.SpeechSynthesizerListener; 26 | import com.baidu.tts.client.SynthesizerTool; 27 | import com.baidu.tts.client.TtsMode; 28 | 29 | import org.json.JSONException; 30 | import org.json.JSONObject; 31 | 32 | import java.io.File; 33 | import java.util.ArrayList; 34 | import java.util.HashMap; 35 | 36 | 37 | public class MainActivity extends AppCompatActivity implements 38 | MainFragment.OnInputBtnClickListner, 39 | VoiceInputFragment.OnRecogResultListener, 40 | SpeechSynthesizerListener { 41 | 42 | private static String TAG = "MainActivity"; 43 | private VoiceInputFragment mVoiceInputFragment; 44 | private EventManager mWakeupManager; 45 | private WebView mWebView; 46 | private TextView mInfoText; 47 | private RequestQueue mVolleyQueue; 48 | private SpeechSynthesizer mSpeechSynthesizer; 49 | 50 | private static final int MEDIA_FREE = 0; 51 | private static final int MEDIA_ASRING = 1; 52 | private static final int MEDIA_TTSING = 2; 53 | private static final int MEDIA_WATING_WAKEUP = 3; 54 | 55 | private int mMediaStatus; 56 | 57 | @Override 58 | protected void onCreate(Bundle savedInstanceState) { 59 | super.onCreate(savedInstanceState); 60 | setContentView(R.layout.activity_main); 61 | mMediaStatus = MEDIA_FREE; 62 | mWebView = (WebView) findViewById(R.id.webview); 63 | mInfoText = (TextView) findViewById(R.id.info_text); 64 | 65 | // 设置 WebView 66 | WebSettings settings = mWebView.getSettings(); 67 | // 设置启用 JS 68 | settings.setJavaScriptEnabled(true); 69 | // 设置缓存模式 70 | settings.setDatabaseEnabled(true); 71 | settings.setDomStorageEnabled(true); 72 | settings.setAppCacheEnabled(true); 73 | settings.setCacheMode(WebSettings.LOAD_DEFAULT); 74 | settings.setAppCachePath(mWebView.getContext().getCacheDir().getAbsolutePath()); 75 | mWebView.canGoBack(); 76 | mWebView.setWebViewClient(new WebViewClientWrapper()); 77 | 78 | // 设置 INFO 块自动语音播报 79 | mInfoText.addTextChangedListener(new TextWatcher() { 80 | @Override 81 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 82 | 83 | } 84 | 85 | @Override 86 | public void onTextChanged(CharSequence s, int start, int before, int count) { 87 | getSpeechSynthesizer().speak(s.subSequence(start, start + count).toString()); 88 | } 89 | 90 | @Override 91 | public void afterTextChanged(Editable s) { 92 | 93 | } 94 | }); 95 | // 默认启动识别 96 | findViewById(R.id.input_btn).callOnClick(); 97 | } 98 | 99 | @Override 100 | protected void onPause() { 101 | // 停止视频播放 102 | mWebView.onPause(); 103 | // 停止播报 104 | if (mSpeechSynthesizer != null) { 105 | mSpeechSynthesizer.pause(); 106 | } 107 | super.onPause(); 108 | } 109 | 110 | @Override 111 | protected void onResume() { 112 | super.onResume(); 113 | // 默认启动识别 114 | // findViewById(R.id.input_btn).callOnClick(); 115 | } 116 | 117 | @Override 118 | protected void onStop() { 119 | if (mSpeechSynthesizer != null) { 120 | mSpeechSynthesizer.stop(); 121 | } 122 | super.onStop(); 123 | } 124 | 125 | @Override 126 | protected void onRestart() { 127 | super.onRestart(); 128 | } 129 | 130 | @Override 131 | protected void onDestroy() { 132 | if (mSpeechSynthesizer != null) { 133 | mSpeechSynthesizer.release(); 134 | } 135 | super.onDestroy(); 136 | } 137 | 138 | @Override 139 | public void onInputBtnClick(View v) { 140 | if (mVoiceInputFragment == null) { 141 | mVoiceInputFragment = new VoiceInputFragment(); 142 | } 143 | // 停止唤醒词 144 | stopWaitingWakeup(); 145 | // 停止 webview 视频播放 146 | mWebView.onPause(); 147 | // 隐藏报错信息 148 | hideInfo(); 149 | // 直接使用成员变量,避免初始化开销,待优化s 150 | if (mSpeechSynthesizer != null) { 151 | mSpeechSynthesizer.stop(); 152 | } 153 | 154 | 155 | android.support.v4.app.FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 156 | transaction.replace(R.id.main_frame, mVoiceInputFragment); 157 | transaction.addToBackStack(null); 158 | transaction.commit(); 159 | } 160 | 161 | public void onRecogResult(Bundle bundle) { 162 | getSupportFragmentManager().popBackStack(); 163 | 164 | TextView inputText = (TextView) findViewById(R.id.input_text); 165 | WebView webView = (WebView) findViewById(R.id.webview); 166 | 167 | Log.d("", "onRecogResult: " + bundle.toString()); 168 | 169 | Integer error = bundle.getInt("error"); 170 | if (0 == error) { 171 | ArrayList results = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); 172 | if (results.size() > 0) { 173 | String query = results.get(0); 174 | inputText.setText(query); 175 | String what = QueryParser.getWhat(query); 176 | 177 | if (what.length() != 0) { 178 | BaiduBaikeHandler baikeHandler = new BaiduBaikeHandler(this); 179 | baikeHandler.search(what); 180 | } else { 181 | showInfo(String.format(getResources().getString(R.string.error_on_input_format), query)); 182 | } 183 | } 184 | } else { 185 | int errStringId = getResources().getIdentifier("vsr_error_" + error.toString(), "string", getPackageName()); 186 | String errMsg = getResources().getString(errStringId); 187 | String infoMsg = getResources().getString(R.string.error_on_voice_input); 188 | showInfo(String.format(infoMsg, errMsg)); 189 | 190 | } 191 | // 开始监听唤醒词 192 | startWaitingWakeup(); 193 | // 继续 WebView 视频播放 194 | mWebView.onResume(); 195 | } 196 | 197 | public void showInfo(String info) { 198 | mInfoText.setText(info); 199 | if (mInfoText.getVisibility() != View.VISIBLE) { 200 | mInfoText.setVisibility(View.VISIBLE); 201 | } 202 | } 203 | 204 | public void showInfo(int id) { 205 | mInfoText.setText(id); 206 | } 207 | 208 | public void hideInfo() { 209 | if (mInfoText.getVisibility() != View.GONE) { 210 | mInfoText.setVisibility(View.GONE); 211 | } 212 | } 213 | 214 | public void startWaitingWakeup() { 215 | Log.i(TAG, "startWaitingWakeup: "); 216 | // 通知唤醒管理器, 启动唤醒功能 217 | HashMap params = new HashMap(); 218 | // 设置唤醒资源, 唤醒资源请到 http://yuyin.baidu.com/wake#m4 来评估和导出 219 | params.put("kws-file", "assets:///WakeUp.bin"); 220 | getWpEventManager().send("wp.start", new JSONObject(params).toString(), null, 0, 0); 221 | } 222 | 223 | public void stopWaitingWakeup() { 224 | Log.i(TAG, "stopWaitingWakeup: "); 225 | // 停止唤醒监听 226 | getWpEventManager().send("wp.stop", null, null, 0, 0); 227 | } 228 | 229 | public RequestQueue getVolleyQueue() { 230 | if (mVolleyQueue == null) { 231 | // 使用支持代理和重定向的 HurlStack 232 | mVolleyQueue = Volley.newRequestQueue(this, new HurlStackWrapper()); 233 | } 234 | return mVolleyQueue; 235 | } 236 | 237 | public SpeechSynthesizer getSpeechSynthesizer() { 238 | Log.i(TAG, "getSpeechSynthesizer:"); 239 | if (mSpeechSynthesizer == null) { 240 | Log.i(TAG, "getSpeechSynthesizer: Initialize."); 241 | // 获取语音合成对象实例 242 | mSpeechSynthesizer = SpeechSynthesizer.getInstance(); 243 | // 设置context 244 | mSpeechSynthesizer.setContext(this); 245 | // 设置语音合成状态监听器 246 | mSpeechSynthesizer.setSpeechSynthesizerListener(this); 247 | 248 | // 设置离线引擎的资源,因为资源文件太大,需要拷贝到SD卡上才能使用 249 | // 设置语音合成文本模型文件(离线引擎使用) 250 | File textModelFile = new File(getExternalFilesDir(null), "bd_etts_text.dat"); 251 | Utils.copyAssetsToSdcard(getAssets(), "bd_etts_text.dat", textModelFile); 252 | mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE, textModelFile.getPath()); 253 | // 设置语音合成声学模型文件(离线引擎使用) 254 | File speechModelFile = new File(getExternalFilesDir(null), "bd_etts_speech_female.dat"); 255 | Utils.copyAssetsToSdcard(getAssets(), "bd_etts_speech_female.dat", speechModelFile); 256 | mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE, speechModelFile.getPath()); 257 | 258 | // 拿到 APP 的授权信息,并设置授权 259 | mSpeechSynthesizer.setApiKey(getResources().getString(R.string.api_key), 260 | getResources().getString(R.string.secret_key)); 261 | mSpeechSynthesizer.setAppId(getResources().getString(R.string.app_id)); 262 | 263 | // 发音人(在线引擎),可用参数为0,1,2,3。。。(服务器端会动态增加,各值含义参考文档, 264 | // 以文档说明为准。0--普通女声,1--普通男声,2--特别男声,3--情感男声,4--情感儿童声<度丫丫> 265 | mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "4"); 266 | mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_MIX_MODE, 267 | SpeechSynthesizer.MIX_MODE_HIGH_SPEED_NETWORK); 268 | mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "6"); 269 | mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "6"); 270 | 271 | // 初始化语音合成 272 | mSpeechSynthesizer.initTts(TtsMode.MIX); 273 | 274 | Log.d(TAG, "getSpeechSynthesizer: engineVersion=" + SynthesizerTool.getEngineVersion()); 275 | Log.d(TAG, "getSpeechSynthesizer: engineInfo=" + SynthesizerTool.getEngineInfo()); 276 | Log.d(TAG, "getSpeechSynthesizer: textModelInfo=" + SynthesizerTool.getModelInfo(textModelFile.getPath())); 277 | Log.d(TAG, "getSpeechSynthesizer: speechModelInfo=" + SynthesizerTool.getModelInfo(speechModelFile.getPath())); 278 | } 279 | return mSpeechSynthesizer; 280 | } 281 | 282 | public EventManager getWpEventManager() { 283 | if (mWakeupManager == null) { 284 | // 1) 创建唤醒事件管理器 285 | mWakeupManager = EventManagerFactory.create(this, "wp"); 286 | // 2) 注册唤醒事件监听器 287 | mWakeupManager.registerListener(new EventListener() { 288 | @Override 289 | public void onEvent(String name, String params, byte[] data, int offset, int length) { 290 | Log.i(TAG, "onEvent: name=" + name); 291 | try { 292 | JSONObject json = new JSONObject(params); 293 | if ("wp.data".equals(name)) { 294 | // 每次唤醒成功, 将会回调name=wp.data的时间, 被激活的唤醒词在params的word字段 295 | String word = json.getString("word"); // 唤醒词 296 | Log.i(TAG, "WakeUp by word=" + word); 297 | findViewById(R.id.input_btn).callOnClick(); 298 | } else if ("wp.exit".equals(name)) { 299 | // 唤醒已经停止 300 | } 301 | } catch (JSONException e) { 302 | throw new AndroidRuntimeException(e); 303 | } 304 | } 305 | }); 306 | } 307 | return mWakeupManager; 308 | } 309 | 310 | @Override 311 | public void onError(String arg0, SpeechError arg1) { 312 | // 监听到出错,在此添加相关操作 313 | Log.w(TAG, "onError: SpeechSynthesizer error: " + arg1.toString()); 314 | } 315 | 316 | @Override 317 | public void onSpeechFinish(String arg0) { 318 | // 监听到播放结束,在此添加相关操作 319 | Log.d(TAG, "onSpeechFinish: "); 320 | runOnUiThread(new Runnable() { 321 | @Override 322 | public void run() { 323 | hideInfo(); 324 | mWebView.onResume(); 325 | } 326 | }); 327 | } 328 | 329 | @Override 330 | public void onSpeechProgressChanged(String arg0, int arg1) { 331 | // 监听到播放进度有变化,在此添加相关操作 332 | } 333 | 334 | @Override 335 | public void onSpeechStart(String arg0) { 336 | // 监听到合成并播放开始,在此添加相关操作 337 | Log.d(TAG, "onSpeechStart: "); 338 | runOnUiThread(new Runnable() { 339 | @Override 340 | public void run() { 341 | mWebView.onPause(); 342 | } 343 | }); 344 | } 345 | 346 | @Override 347 | public void onSynthesizeDataArrived(String arg0, byte[] arg1, int arg2) { 348 | // 监听到有合成数据到达,在此添加相关操作 349 | } 350 | 351 | @Override 352 | public void onSynthesizeFinish(String arg0) { 353 | // 监听到合成结束,在此添加相关操作 354 | } 355 | 356 | @Override 357 | public void onSynthesizeStart(String arg0) { 358 | // 监听到合成开始,在此添加相关操作 359 | } 360 | } 361 | --------------------------------------------------------------------------------