├── .gitignore ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ailib ├── build.gradle ├── gradle.properties ├── proguard-rules.txt └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── ai │ │ │ └── api │ │ │ ├── AIListener.java │ │ │ ├── DefaultAIListener.java │ │ │ ├── PartialResultsListener.java │ │ │ ├── android │ │ │ ├── AIConfiguration.java │ │ │ ├── AIDataService.java │ │ │ ├── AIService.java │ │ │ ├── GsonFactory.java │ │ │ └── SessionIdStorage.java │ │ │ ├── services │ │ │ ├── GoogleRecognitionServiceImpl.java │ │ │ └── SpeaktoitRecognitionServiceImpl.java │ │ │ ├── ui │ │ │ ├── AIButton.java │ │ │ ├── AIDialog.java │ │ │ ├── MaskedColorView.java │ │ │ ├── SoundLevelButton.java │ │ │ └── SoundLevelCircleDrawable.java │ │ │ └── util │ │ │ ├── BluetoothController.java │ │ │ ├── RecognizerChecker.java │ │ │ ├── VersionConfig.java │ │ │ └── VoiceActivityDetector.java │ └── res │ │ ├── color │ │ └── mic_colors.xml │ │ ├── drawable-xxhdpi │ │ ├── cube.png │ │ ├── microphone_control.png │ │ ├── speaker.png │ │ └── speaker_silent.png │ │ ├── drawable │ │ └── mic_control.xml │ │ ├── layout │ │ └── aidialog.xml │ │ ├── raw │ │ └── version_config.json │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ ├── java │ └── ai │ │ └── api │ │ └── test │ │ ├── ProtocolProdTest.java │ │ ├── ProtocolTestBase.java │ │ ├── VADTest.java │ │ └── compatibility │ │ ├── SimpleProtocolTestingService.java │ │ ├── V20150415ProtocolTest.java │ │ ├── default_protocol_model │ │ ├── AIResponseDefault.java │ │ ├── MetadataDefault.java │ │ └── ResultDefault.java │ │ └── v20150204_protocol_model │ │ ├── AIResponseV20150204.java │ │ └── ResultV20150204.java │ └── resources │ ├── debug0.wav │ ├── log.raw │ ├── noiseAndNotification.raw │ ├── noiseOnly.raw │ ├── silence.raw │ ├── speech.raw │ └── what_is_your_name.raw ├── apiAISampleApp ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── ai │ │ └── api │ │ └── sample │ │ ├── AIApplication.java │ │ ├── AIButtonSampleActivity.java │ │ ├── AIDialogSampleActivity.java │ │ ├── AISampleAppWidget.java │ │ ├── AIServiceSampleActivity.java │ │ ├── AISettingsActivity.java │ │ ├── AITextSampleActivity.java │ │ ├── AIWidgetActivity.java │ │ ├── BaseActivity.java │ │ ├── Config.java │ │ ├── LanguageConfig.java │ │ ├── MainActivity.java │ │ ├── SettingsManager.java │ │ └── TTS.java │ └── res │ ├── drawable-hdpi │ ├── ic_launcher.png │ └── recording_shape.xml │ ├── drawable-mdpi │ └── ic_launcher.png │ ├── drawable-xhdpi │ └── ic_launcher.png │ ├── drawable-xxhdpi │ └── ic_launcher.png │ ├── drawable │ └── view_alpha_selector.xml │ ├── layout │ ├── activity_aibutton_sample.xml │ ├── activity_aidialog_sample.xml │ ├── activity_aiservice_sample.xml │ ├── activity_aitext_sample.xml │ ├── activity_main.xml │ ├── activity_settings.xml │ ├── activity_widget_sample.xml │ └── ai_app_widget.xml │ ├── menu │ ├── main.xml │ ├── menu_aibutton_sample.xml │ └── menu_aiservice_sample.xml │ ├── raw │ ├── test_cancel.wav │ ├── test_start.wav │ └── test_stop.mp3 │ ├── values-w820dp │ └── dimens.xml │ ├── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── ai_app_widget_info.xml ├── build.gradle ├── docs ├── images │ ├── AppScreenshot.png │ ├── BuildTools19.1.png │ ├── DebugButton.png │ ├── Dependencies.png │ ├── Dependencies2.png │ ├── LayoutDesigner.png │ ├── ListenButton.png │ ├── MinSDK.png │ ├── NewModule.png │ ├── NewModuleDlg.png │ ├── NewProject.png │ ├── NewProjectDialog.png │ ├── PlainText.png │ ├── ResultViewText.png │ ├── SDKManager.png │ └── apiKeys.png └── integration.md ├── gradle-mvn-push.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 Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | ailib/build 19 | sampleApp/build 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Android Studio files 28 | .idea 29 | *.iml 30 | 31 | .DS_Store -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | sudo: false 4 | 5 | android: 6 | components: 7 | # Uncomment the lines below if you want to 8 | # use the latest revision of Android SDK Tools 9 | - platform-tools 10 | - tools 11 | 12 | # The BuildTools version used by your project 13 | - build-tools-25.0.1 14 | 15 | # The SDK version used to compile your project 16 | - android-24 17 | 18 | # Additional components 19 | - extra-google-google_play_services 20 | - extra-google-m2repository 21 | - extra-android-m2repository 22 | - addon-google_apis-google-23 23 | 24 | # Specify at least one system image, 25 | # if you need to run emulator(s) during your tests 26 | - sys-img-armeabi-v7a-android-19 27 | - sys-img-x86-android-17 28 | branches: 29 | only: 30 | - master 31 | - dev 32 | 33 | script: 34 | - echo "Travis branch is $TRAVIS_BRANCH" 35 | - echo "Travis branch is in pull request $TRAVIS_PULL+REQUEST" 36 | - ./gradlew test -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your sample apps and patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please fill out either the individual or corporate Contributor License Agreement 9 | (CLA). 10 | 11 | * If you are an individual writing original source code and you're sure you 12 | own the intellectual property, then you'll need to sign an [individual CLA](https://developers.google.com/open-source/cla/individual). 13 | * If you work for a company that wants to allow you to contribute your work, 14 | then you'll need to sign a [corporate CLA](https://developers.google.com/open-source/cla/corporate). 15 | 16 | Follow either of the two links above to access the appropriate CLA and 17 | instructions for how to sign and return it. Once we receive it, we'll be able to 18 | accept your pull requests. 19 | 20 | ## Contributing A Patch 21 | 22 | 1. Submit an issue describing your proposed change to the repo in question. 23 | 1. The repo owner will respond to your issue promptly. 24 | 1. If your proposed change is accepted, and you haven't already done so, sign a 25 | Contributor License Agreement (see details above). 26 | 1. Fork the desired repo, develop and test your code changes. 27 | 1. Ensure that your code adheres to the existing style in the sample to which 28 | you are contributing. Refer to the 29 | [Google Cloud Platform Samples Style Guide](https://github.com/GoogleCloudPlatform/Template/wiki/style.html) for the 30 | recommended coding standards for this organization. 31 | 1. Ensure that your code has an appropriate set of unit tests which all pass. 32 | 1. Submit a pull request. 33 | -------------------------------------------------------------------------------- /ailib/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | 4 | android { 5 | compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) 6 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION 7 | 8 | defaultConfig { 9 | minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) 10 | targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) 11 | versionCode Integer.parseInt(project.VERSION_CODE) 12 | versionName project.VERSION_NAME 13 | } 14 | compileOptions { 15 | sourceCompatibility JavaVersion.VERSION_1_7 16 | targetCompatibility JavaVersion.VERSION_1_7 17 | } 18 | buildTypes { 19 | release { 20 | // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 21 | buildConfigField "int", "TESTS_SDK", "22" 22 | } 23 | debug{ 24 | buildConfigField "int", "TESTS_SDK", "22" 25 | } 26 | } 27 | productFlavors { 28 | } 29 | 30 | packagingOptions { 31 | } 32 | } 33 | 34 | dependencies { 35 | repositories { 36 | mavenLocal() 37 | mavenCentral() 38 | } 39 | compile fileTree(dir: 'libs', include: ['*.jar']) 40 | compile 'com.android.support:support-v4:23.2.1' 41 | compile 'ai.api:libai:1.6.11' 42 | 43 | testCompile 'org.slf4j:slf4j-android:1.7.25' 44 | testCompile "junit:junit:4.12" 45 | testCompile "org.assertj:assertj-core:1.7.0" 46 | testCompile "org.robolectric:robolectric:3.1.2" 47 | } 48 | 49 | apply from: '../gradle-mvn-push.gradle' 50 | 51 | // block for building library jars 52 | android.libraryVariants.all { variant -> 53 | def name = variant.buildType.name 54 | def task = project.tasks.create "jar${name.capitalize()}", Jar 55 | task.dependsOn variant.javaCompile 56 | task.from variant.javaCompile.destinationDir 57 | artifacts.add('archives', task); 58 | } -------------------------------------------------------------------------------- /ailib/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=API.AI Android SDK 2 | POM_ARTIFACT_ID=sdk 3 | POM_PACKAGING=aar -------------------------------------------------------------------------------- /ailib/proguard-rules.txt: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/xvir/sdk/android/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | -keep class ai.api.model.** { *; } 20 | 21 | ##---------------Begin: proguard configuration for Gson ---------- 22 | # Gson uses generic type information stored in a class file when working with fields. Proguard 23 | # removes such information by default, so configure it to keep all of it. 24 | -keepattributes Signature 25 | 26 | # For using GSON @Expose annotation 27 | -keepattributes *Annotation* 28 | 29 | # Gson specific classes 30 | -keep class sun.misc.Unsafe { *; } 31 | 32 | ##---------------End: proguard configuration for Gson ---------- -------------------------------------------------------------------------------- /ailib/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/AIListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api; 18 | 19 | import ai.api.android.AIService; 20 | import ai.api.model.AIError; 21 | import ai.api.model.AIResponse; 22 | 23 | /** 24 | * Listener interface for working with API.AI service. Create implementation of it and use with {@link AIService AIService} 25 | */ 26 | public interface AIListener { 27 | 28 | /** 29 | * Event fires when entire process finished successfully, and returns result object 30 | * 31 | * @param result the result object, contains server answer 32 | */ 33 | void onResult(AIResponse result); 34 | 35 | /** 36 | * Event fires if something going wrong while recognition or access to the AI server 37 | * 38 | * @param error the error description object 39 | */ 40 | void onError(AIError error); 41 | 42 | /** 43 | * Event fires every time sound level changed. Use it to create visual feedback. There is no guarantee that this method will 44 | * be called. 45 | * 46 | * @param level the new RMS dB value 47 | */ 48 | void onAudioLevel(float level); 49 | 50 | /** 51 | * Event fires when recognition engine start listening 52 | */ 53 | void onListeningStarted(); 54 | 55 | /** 56 | * Event fires when recognition engine cancel listening 57 | */ 58 | void onListeningCanceled(); 59 | 60 | /** 61 | * Event fires when recognition engine finish listening 62 | */ 63 | void onListeningFinished(); 64 | } 65 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/DefaultAIListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api; 18 | 19 | /** 20 | * Use this class instead of AIListener if you don't want to process all recognition events. Only onResult and onError required. 21 | */ 22 | public abstract class DefaultAIListener implements AIListener { 23 | 24 | @Override 25 | public void onAudioLevel(final float level) { 26 | 27 | } 28 | 29 | @Override 30 | public void onListeningStarted() { 31 | 32 | } 33 | 34 | @Override 35 | public void onListeningFinished() { 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/PartialResultsListener.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api; 18 | 19 | import java.util.List; 20 | 21 | public interface PartialResultsListener { 22 | void onPartialResults(final List partialResults); 23 | } 24 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/android/AIConfiguration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.android; 18 | 19 | import android.content.res.AssetFileDescriptor; 20 | import android.text.TextUtils; 21 | 22 | import java.net.Proxy; 23 | 24 | public class AIConfiguration extends ai.api.AIConfiguration { 25 | public enum RecognitionEngine { 26 | 27 | /** 28 | * Google Speech Recognition integrated into Android OS 29 | * 30 | * @deprecated Use System instead 31 | */ 32 | @Deprecated 33 | Google, 34 | 35 | /** 36 | * Default system recognition 37 | */ 38 | System, 39 | 40 | /** 41 | * Speaktoit recognition engine 42 | * 43 | * @deprecated Use System instead 44 | * will be support until 01.02.2017 45 | */ 46 | @Deprecated 47 | Speaktoit 48 | } 49 | 50 | private final RecognitionEngine recognitionEngine; 51 | 52 | /** 53 | * Speaktoit recognition start sound resouce descriptor 54 | */ 55 | private AssetFileDescriptor recognizerStartSound; 56 | 57 | /** 58 | * Speaktoit recognition stop sound resouce descriptor 59 | */ 60 | private AssetFileDescriptor recognizerStopSound; 61 | 62 | /** 63 | * Speaktoit recognition cancel sound resource descriptor 64 | */ 65 | private AssetFileDescriptor recognizerCancelSound; 66 | 67 | private boolean voiceActivityDetectionEnabled = true; 68 | 69 | private boolean normalizeInputSound = false; 70 | 71 | public AIConfiguration(final String clientAccessToken, final SupportedLanguages language, final RecognitionEngine recognitionEngine) { 72 | super(clientAccessToken, language); 73 | 74 | this.recognitionEngine = recognitionEngine; 75 | 76 | if (recognitionEngine == RecognitionEngine.Speaktoit 77 | && language == SupportedLanguages.Korean) { 78 | throw new UnsupportedOperationException("Only System recognition supported for Korean language"); 79 | } 80 | } 81 | 82 | public RecognitionEngine getRecognitionEngine() { 83 | return recognitionEngine; 84 | } 85 | 86 | public boolean isVoiceActivityDetectionEnabled() { 87 | return voiceActivityDetectionEnabled; 88 | } 89 | 90 | /** 91 | * With setting this field to false you can disable voice activity detection for Speaktoit recognition. 92 | * This option does not affect System recognition. 93 | * @param voiceActivityDetectionEnabled 94 | */ 95 | public void setVoiceActivityDetectionEnabled(final boolean voiceActivityDetectionEnabled) { 96 | this.voiceActivityDetectionEnabled = voiceActivityDetectionEnabled; 97 | } 98 | 99 | /** 100 | * With setting this field to true you can enable sound amplification if it's too quiet. This option improves recognition quality on some devices. 101 | * This option does not affect System recognition. 102 | * @param normalizeInputSound 103 | */ 104 | public void setNormalizeInputSound(final boolean normalizeInputSound) { 105 | this.normalizeInputSound = normalizeInputSound; 106 | } 107 | 108 | public boolean isNormalizeInputSound() { 109 | return normalizeInputSound; 110 | } 111 | 112 | public AssetFileDescriptor getRecognizerStartSound() { 113 | return recognizerStartSound; 114 | } 115 | 116 | public void setRecognizerStartSound(final AssetFileDescriptor recognizerStartSound) { 117 | this.recognizerStartSound = recognizerStartSound; 118 | } 119 | 120 | public AssetFileDescriptor getRecognizerStopSound() { 121 | return recognizerStopSound; 122 | } 123 | 124 | public void setRecognizerStopSound(final AssetFileDescriptor recognizerStopSound) { 125 | this.recognizerStopSound = recognizerStopSound; 126 | } 127 | 128 | public AssetFileDescriptor getRecognizerCancelSound() { 129 | return recognizerCancelSound; 130 | } 131 | 132 | public void setRecognizerCancelSound(final AssetFileDescriptor recognizerCancelSound) { 133 | this.recognizerCancelSound = recognizerCancelSound; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/android/AIDataService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.android; 18 | 19 | import android.content.Context; 20 | import android.support.annotation.NonNull; 21 | 22 | import com.google.gson.Gson; 23 | 24 | import java.util.TimeZone; 25 | 26 | import ai.api.AIServiceContext; 27 | 28 | /** 29 | * Do simple requests to the AI Service 30 | */ 31 | public class AIDataService extends ai.api.AIDataService { 32 | 33 | 34 | public static final String TAG = AIDataService.class.getName(); 35 | 36 | @NonNull 37 | private final Context context; 38 | 39 | @NonNull 40 | private final AIConfiguration config; 41 | 42 | @NonNull 43 | private final Gson gson = GsonFactory.getGson(); 44 | 45 | public AIDataService(@NonNull final Context context, @NonNull final AIConfiguration config) { 46 | super(config, new AIAndroidServiceContext(context)); 47 | this.context = context; 48 | this.config = config; 49 | } 50 | 51 | private static class AIAndroidServiceContext implements AIServiceContext { 52 | 53 | private final String sessionId; 54 | 55 | public AIAndroidServiceContext(final Context context) { 56 | sessionId = SessionIdStorage.getSessionId(context); 57 | } 58 | 59 | public String getSessionId() { 60 | return sessionId; 61 | } 62 | 63 | public TimeZone getTimeZone() { return TimeZone.getDefault(); } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/android/AIService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.android; 18 | 19 | import android.Manifest; 20 | import android.content.Context; 21 | 22 | import java.util.Collection; 23 | import java.util.List; 24 | 25 | import ai.api.AIListener; 26 | import ai.api.AIServiceException; 27 | import ai.api.RequestExtras; 28 | import ai.api.model.AIContext; 29 | import ai.api.model.AIError; 30 | import ai.api.model.AIRequest; 31 | import ai.api.model.AIResponse; 32 | import ai.api.model.Entity; 33 | import ai.api.services.GoogleRecognitionServiceImpl; 34 | import ai.api.services.SpeaktoitRecognitionServiceImpl; 35 | import android.content.pm.PackageManager; 36 | import android.support.v4.content.ContextCompat; 37 | 38 | /** 39 | * Main SDK class for working with API.AI service. 40 | */ 41 | public abstract class AIService { 42 | 43 | private static final String TAG = AIService.class.getName(); 44 | 45 | protected final AIConfiguration config; 46 | protected final Context context; 47 | 48 | protected final AIDataService aiDataService; 49 | 50 | private AIListener listener; 51 | 52 | /** 53 | * Use this method to get ready to work instance 54 | * @param context 55 | * @param config 56 | * @return instance of AIService implementation 57 | */ 58 | public static AIService getService(final Context context, final AIConfiguration config) { 59 | if (config.getRecognitionEngine() == AIConfiguration.RecognitionEngine.Google) { 60 | return new GoogleRecognitionServiceImpl(context, config); 61 | } 62 | if (config.getRecognitionEngine() == AIConfiguration.RecognitionEngine.System) { 63 | return new GoogleRecognitionServiceImpl(context, config); 64 | } 65 | else if (config.getRecognitionEngine() == AIConfiguration.RecognitionEngine.Speaktoit) { 66 | return new SpeaktoitRecognitionServiceImpl(context, config); 67 | } else { 68 | throw new UnsupportedOperationException("This engine still not supported"); 69 | } 70 | } 71 | 72 | protected AIService(final AIConfiguration config, final Context context) { 73 | this.config = config; 74 | this.context = context; 75 | 76 | aiDataService = new AIDataService(context, config); 77 | } 78 | 79 | /** 80 | * Starts listening process 81 | */ 82 | public abstract void startListening(); 83 | 84 | /** 85 | * Starts listening process. Request to the AI service will be done with specified contexts. 86 | */ 87 | public abstract void startListening(List contexts); 88 | 89 | /** 90 | * Starts listening process. Request to the AI service will be done with specified extra data. 91 | * @param requestExtras extras can hold additional contexts and entities 92 | */ 93 | public abstract void startListening(RequestExtras requestExtras); 94 | 95 | /** 96 | * Stop listening and start request to the AI service with current recognition results 97 | */ 98 | public abstract void stopListening(); 99 | 100 | /** 101 | * Cancel listening process and don't request to AI service 102 | */ 103 | public abstract void cancel(); 104 | 105 | /** 106 | * Sets listener, which used to notify about process steps 107 | * @param listener {@link AIListener AIListener} implementation 108 | */ 109 | public void setListener(final AIListener listener) { 110 | this.listener = listener; 111 | } 112 | 113 | protected void onResult(final AIResponse response) { 114 | if (listener != null) { 115 | listener.onResult(response); 116 | } 117 | } 118 | 119 | protected void onError(final AIError error) { 120 | if (listener != null) { 121 | listener.onError(error); 122 | } 123 | } 124 | 125 | protected void onAudioLevelChanged(final float audioLevel) { 126 | if (listener != null) { 127 | listener.onAudioLevel(audioLevel); 128 | } 129 | } 130 | 131 | protected void onListeningStarted() { 132 | if (listener != null) { 133 | listener.onListeningStarted(); 134 | } 135 | } 136 | 137 | protected void onListeningCancelled() { 138 | if (listener != null) { 139 | listener.onListeningCanceled(); 140 | } 141 | } 142 | 143 | protected void onListeningFinished() { 144 | if (listener != null) { 145 | listener.onListeningFinished(); 146 | } 147 | } 148 | 149 | public void pause() { 150 | 151 | } 152 | 153 | public void resume(){ 154 | 155 | } 156 | 157 | public AIResponse textRequest(final AIRequest request) throws AIServiceException { 158 | return aiDataService.request(request); 159 | } 160 | 161 | public AIResponse textRequest(final String textRequest, final RequestExtras requestExtras) throws AIServiceException { 162 | final AIRequest aiRequest = new AIRequest(textRequest); 163 | if (requestExtras != null) { 164 | requestExtras.copyTo(aiRequest); 165 | } 166 | return aiDataService.request(aiRequest); 167 | } 168 | 169 | /** 170 | * Forget all old contexts 171 | * @return true if operation succeed, false otherwise 172 | */ 173 | public boolean resetContexts() { 174 | return aiDataService.resetContexts(); 175 | } 176 | 177 | /** 178 | * Upload user entity for using while session 179 | * @param userEntity entity to upload 180 | * @return uploading result 181 | * @throws AIServiceException 182 | */ 183 | public AIResponse uploadUserEntity(final Entity userEntity) throws AIServiceException { 184 | return aiDataService.uploadUserEntity(userEntity); 185 | } 186 | 187 | /** 188 | * Upload user entities for using while session 189 | * @param userEntities collection of user entities 190 | * @return uploading result 191 | * @throws AIServiceException if request to the API.AI service failed 192 | */ 193 | public AIResponse uploadUserEntities(final Collection userEntities) throws AIServiceException { 194 | return aiDataService.uploadUserEntities(userEntities); 195 | } 196 | 197 | protected boolean checkPermissions() { 198 | boolean granted = true; 199 | try { 200 | granted = ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED; 201 | } catch (final Throwable ignored) { 202 | } 203 | return granted; 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/android/GsonFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.android; 18 | 19 | import com.google.gson.Gson; 20 | import com.google.gson.GsonBuilder; 21 | 22 | import java.text.SimpleDateFormat; 23 | import java.util.Locale; 24 | 25 | public class GsonFactory { 26 | 27 | public static Gson getGson(){ 28 | return ai.api.GsonFactory.getDefaultFactory().getGson(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/android/SessionIdStorage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.android; 18 | 19 | import android.content.Context; 20 | import android.content.SharedPreferences; 21 | import android.text.TextUtils; 22 | 23 | import java.util.UUID; 24 | 25 | public abstract class SessionIdStorage { 26 | private static final String PREF_NAME = "APIAI_preferences"; 27 | private static final String SESSION_ID = "sessionId"; 28 | 29 | public synchronized static String getSessionId(final Context context) { 30 | final SharedPreferences sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); 31 | final String sessionId = sharedPreferences.getString(SESSION_ID, ""); 32 | if (!TextUtils.isEmpty(sessionId)) { 33 | return sessionId; 34 | } else { 35 | final SharedPreferences.Editor editor = sharedPreferences.edit(); 36 | final String value = UUID.randomUUID().toString(); 37 | editor.putString(SESSION_ID, value); 38 | editor.commit(); 39 | return value; 40 | } 41 | 42 | } 43 | 44 | public static synchronized void resetSessionId(final Context context) { 45 | final SharedPreferences sharedPreferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); 46 | final SharedPreferences.Editor editor = sharedPreferences.edit(); 47 | editor.putString(SESSION_ID, ""); 48 | editor.commit(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/ui/AIDialog.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.ui; 18 | 19 | import android.app.Dialog; 20 | import android.content.Context; 21 | import android.os.Handler; 22 | import android.os.Looper; 23 | import android.text.TextUtils; 24 | import android.view.Window; 25 | import android.widget.TextView; 26 | 27 | import java.util.List; 28 | 29 | import ai.api.android.AIConfiguration; 30 | import ai.api.android.AIService; 31 | import ai.api.AIServiceException; 32 | import ai.api.PartialResultsListener; 33 | import ai.api.R; 34 | import ai.api.model.AIError; 35 | import ai.api.model.AIRequest; 36 | import ai.api.model.AIResponse; 37 | 38 | public class AIDialog { 39 | 40 | private static final String TAG = AIDialog.class.getName(); 41 | 42 | private final Context context; 43 | private final AIConfiguration config; 44 | 45 | private AIDialogListener resultsListener; 46 | private final Dialog dialog; 47 | private final AIButton aiButton; 48 | private final TextView partialResultsTextView; 49 | 50 | private final Handler handler; 51 | 52 | public interface AIDialogListener { 53 | void onResult(final AIResponse result); 54 | void onError(final AIError error); 55 | void onCancelled(); 56 | } 57 | 58 | public AIDialog(final Context context, final AIConfiguration config) { 59 | this(context, config, R.layout.aidialog); 60 | } 61 | 62 | public AIDialog(final Context context, final AIConfiguration config, final int customLayout) { 63 | this.context = context; 64 | this.config = config; 65 | dialog = new Dialog(context); 66 | handler = new Handler(Looper.getMainLooper()); 67 | 68 | dialog.setCanceledOnTouchOutside(true); 69 | dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); 70 | dialog.setContentView(customLayout); 71 | 72 | partialResultsTextView = (TextView) dialog.findViewById(R.id.partialResultsTextView); 73 | 74 | aiButton = (AIButton) dialog.findViewById(R.id.micButton); 75 | aiButton.initialize(config); 76 | setAIButtonCallback(aiButton); 77 | } 78 | 79 | public void setResultsListener(final AIDialogListener resultsListener) { 80 | this.resultsListener = resultsListener; 81 | } 82 | 83 | public Dialog getDialog() { 84 | return dialog; 85 | } 86 | 87 | public void showAndListen() { 88 | handler.post(new Runnable() { 89 | @Override 90 | public void run() { 91 | resetControls(); 92 | dialog.show(); 93 | startListening(); 94 | } 95 | }); 96 | } 97 | 98 | public AIResponse textRequest(final AIRequest request) throws AIServiceException { 99 | return aiButton.textRequest(request); 100 | } 101 | 102 | public AIResponse textRequest(final String request) throws AIServiceException { 103 | return textRequest(new AIRequest(request)); 104 | } 105 | 106 | private void resetControls() { 107 | if (partialResultsTextView != null) { 108 | partialResultsTextView.setText(""); 109 | } 110 | } 111 | 112 | private void setAIButtonCallback(final AIButton aiButton) { 113 | aiButton.setResultsListener(new AIButton.AIButtonListener() { 114 | @Override 115 | public void onResult(final AIResponse result) { 116 | 117 | AIDialog.this.close(); 118 | 119 | if (resultsListener != null) { 120 | resultsListener.onResult(result); 121 | } 122 | } 123 | 124 | @Override 125 | public void onError(final AIError error) { 126 | if (resultsListener != null) { 127 | resultsListener.onError(error); 128 | } 129 | } 130 | 131 | @Override 132 | public void onCancelled() { 133 | 134 | AIDialog.this.close(); 135 | 136 | if (resultsListener != null) { 137 | resultsListener.onCancelled(); 138 | } 139 | } 140 | }); 141 | 142 | aiButton.setPartialResultsListener(new PartialResultsListener() { 143 | @Override 144 | public void onPartialResults(final List partialResults) { 145 | final String result = partialResults.get(0); 146 | if (!TextUtils.isEmpty(result)) { 147 | handler.post(new Runnable() { 148 | @Override 149 | public void run() { 150 | if (partialResultsTextView != null) { 151 | partialResultsTextView.setText(result); 152 | } 153 | } 154 | }); 155 | } 156 | } 157 | }); 158 | 159 | } 160 | 161 | private void startListening() { 162 | if (aiButton != null) { 163 | aiButton.startListening(); 164 | } 165 | } 166 | 167 | public void close() { 168 | handler.post(new Runnable() { 169 | @Override 170 | public void run() { 171 | dialog.dismiss(); 172 | } 173 | }); 174 | } 175 | 176 | /** 177 | * Get AIService object for making different data requests 178 | * @return 179 | */ 180 | public AIService getAIService() { 181 | return aiButton.getAIService(); 182 | } 183 | 184 | /** 185 | * Disconnect aiDialog from the recognition service. 186 | * Use pause/resume methods when you have permanent reference to the AIDialog object in your Activity. 187 | * pause() call should be added to the onPause() method of the Activity. 188 | * resume() call should be added to the onResume() method of the Activity. 189 | */ 190 | public void pause() { 191 | if (aiButton != null) { 192 | aiButton.pause(); 193 | } 194 | } 195 | 196 | /** 197 | * Reconnect aiDialog to the recognition service. 198 | * Use pause/resume methods when you have permanent reference to the AIDialog object in your Activity. 199 | * pause() call should be added to the onPause() method of the Activity. 200 | * resume() call should be added to the onResume() method of the Activity. 201 | */ 202 | public void resume() { 203 | if (aiButton != null) { 204 | aiButton.resume(); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/ui/MaskedColorView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.ui; 18 | 19 | import android.annotation.TargetApi; 20 | import android.content.Context; 21 | import android.content.res.ColorStateList; 22 | import android.content.res.TypedArray; 23 | import android.graphics.Color; 24 | import android.graphics.PorterDuff; 25 | import android.os.Build; 26 | import android.util.AttributeSet; 27 | import android.widget.ImageView; 28 | 29 | import ai.api.R; 30 | 31 | /** 32 | * ImageView 33 | * src used as a mask, and MaskedColorView_mainColor as a color or state color list 34 | */ 35 | public class MaskedColorView extends ImageView { 36 | private ColorStateList colorStateList = null; 37 | 38 | public MaskedColorView(final Context context) { 39 | super(context, null); 40 | } 41 | 42 | public MaskedColorView(final Context context, final AttributeSet attrs) { 43 | super(context, attrs, android.R.attr.imageButtonStyle); 44 | configure(attrs); 45 | } 46 | 47 | public MaskedColorView(final Context context, final AttributeSet attrs, final int defStyle) { 48 | super(context, attrs, defStyle); 49 | configure(attrs); 50 | } 51 | 52 | @TargetApi(11) 53 | @Override 54 | protected void drawableStateChanged() { 55 | super.drawableStateChanged(); 56 | setColorFilter(getCurrentColor(getDrawableState()), PorterDuff.Mode.SRC_ATOP); 57 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 58 | jumpDrawablesToCurrentState(); 59 | } 60 | } 61 | 62 | public void setColorStateList(final ColorStateList colorStateList) { 63 | this.colorStateList = colorStateList; 64 | } 65 | 66 | private int getCurrentColor(final int[] stateSet) { 67 | return colorStateList == null 68 | ? Color.MAGENTA 69 | : colorStateList.getColorForState(stateSet, colorStateList.getDefaultColor()); 70 | } 71 | 72 | private void configure(final AttributeSet attrs) { 73 | if (attrs != null) { 74 | final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.MaskedColorView); 75 | try { 76 | final ColorStateList csl = a.getColorStateList(R.styleable.MaskedColorView_mainColor); 77 | if (csl != null) { 78 | colorStateList = csl; 79 | } 80 | } finally { 81 | a.recycle(); 82 | } 83 | } 84 | } 85 | 86 | protected String getDebugState() { 87 | return "====\ncsl is " + (colorStateList != null ? "NOT" : "") + " null"; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/ui/SoundLevelButton.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.ui; 18 | 19 | import android.annotation.TargetApi; 20 | import android.content.Context; 21 | import android.content.res.TypedArray; 22 | import android.os.Build; 23 | import android.support.annotation.NonNull; 24 | import android.support.annotation.Nullable; 25 | import android.util.AttributeSet; 26 | import android.view.View; 27 | 28 | import ai.api.R; 29 | 30 | public class SoundLevelButton extends MaskedColorView { 31 | 32 | private static final String TAG = SoundLevelButton.class.getName(); 33 | 34 | protected static final int[] STATE_LISTENING = new int[] { R.attr.state_listening }; 35 | 36 | private final SoundLevelCircleDrawable backgroundDrawable; 37 | protected boolean listening = false; 38 | 39 | @SuppressWarnings("UnusedDeclaration") 40 | public SoundLevelButton(@NonNull final Context context) { 41 | super(context); 42 | backgroundDrawable = new SoundLevelCircleDrawable(getParams(context, null)); 43 | setCircleBackground(backgroundDrawable); 44 | init(); 45 | } 46 | 47 | @SuppressWarnings("UnusedDeclaration") 48 | public SoundLevelButton(@NonNull final Context context, @Nullable final AttributeSet attrs) { 49 | super(context, attrs); 50 | backgroundDrawable = new SoundLevelCircleDrawable(getParams(context, attrs)); 51 | setCircleBackground(backgroundDrawable); 52 | init(); 53 | } 54 | 55 | @SuppressWarnings("UnusedDeclaration") 56 | public SoundLevelButton(@NonNull final Context context, @Nullable final AttributeSet attrs, final int defStyle) { 57 | super(context, attrs, defStyle); 58 | backgroundDrawable = new SoundLevelCircleDrawable(getParams(context, attrs)); 59 | setCircleBackground(backgroundDrawable); 60 | init(); 61 | } 62 | 63 | private void init() { 64 | setOnClickListener(new OnClickListener() { 65 | @Override 66 | public void onClick(final View v) { 67 | SoundLevelButton.this.onClick(v); 68 | } 69 | }); 70 | } 71 | 72 | protected void onClick(final View v) { 73 | 74 | } 75 | 76 | @Nullable 77 | private SoundLevelCircleDrawable.Params getParams(@NonNull final Context context, @Nullable final AttributeSet attrs) { 78 | if (attrs != null) { 79 | final TypedArray viewAttrs = context.obtainStyledAttributes(attrs, R.styleable.SoundLevelButton); 80 | 81 | try { 82 | this.listening = viewAttrs.getBoolean(R.styleable.SoundLevelButton_state_listening, false); 83 | 84 | final float maxRadius = viewAttrs.getDimension(R.styleable.SoundLevelButton_maxRadius, -1); 85 | final float minRadius = viewAttrs.getDimension(R.styleable.SoundLevelButton_minRadius, -1); 86 | final float circleCenterX = viewAttrs.getDimension(R.styleable.SoundLevelButton_circleCenterX, -1); 87 | final float circleCenterY = viewAttrs.getDimension(R.styleable.SoundLevelButton_circleCenterY, -1); 88 | final int centerColor = viewAttrs.getColor(R.styleable.SoundLevelButton_centerColor, SoundLevelCircleDrawable.CENTER_COLOR_DEF); 89 | final int haloColor = viewAttrs.getColor(R.styleable.SoundLevelButton_haloColor, SoundLevelCircleDrawable.HALO_COLOR_DEF); 90 | return new SoundLevelCircleDrawable.Params(maxRadius, minRadius, circleCenterX, circleCenterY, centerColor, haloColor); 91 | } finally { 92 | viewAttrs.recycle(); 93 | } 94 | } 95 | return null; 96 | } 97 | 98 | @Override 99 | public int[] onCreateDrawableState(final int extraSpace) { 100 | final int[] state = super.onCreateDrawableState(extraSpace + 1); 101 | if (listening) mergeDrawableStates(state, STATE_LISTENING); 102 | return state; 103 | } 104 | 105 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 106 | private void setCircleBackground(final SoundLevelCircleDrawable soundLevelCircleDrawable) { 107 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) 108 | //noinspection deprecation 109 | setBackgroundDrawable(soundLevelCircleDrawable); 110 | else 111 | setBackground(soundLevelCircleDrawable); 112 | } 113 | 114 | public void setDrawSoundLevel(final boolean drawSoundLevel) { 115 | listening = drawSoundLevel; 116 | backgroundDrawable.setDrawSoundLevel(drawSoundLevel); 117 | refreshDrawableState(); 118 | postInvalidate(); 119 | } 120 | 121 | protected void setDrawCenter(final boolean drawCenter) { 122 | this.backgroundDrawable.setDrawCenter(drawCenter); 123 | } 124 | 125 | public void setSoundLevel(final float soundLevel) { 126 | backgroundDrawable.setSoundLevel(soundLevel); 127 | postInvalidate(); 128 | } 129 | 130 | @Override 131 | protected String getDebugState() { 132 | return super.getDebugState() + "\ndrawSL: " + listening; 133 | } 134 | 135 | protected float getMinRadius() { 136 | return backgroundDrawable.getMinRadius(); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/ui/SoundLevelCircleDrawable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.ui; 18 | 19 | import android.graphics.Canvas; 20 | import android.graphics.Color; 21 | import android.graphics.ColorFilter; 22 | import android.graphics.Paint; 23 | import android.graphics.PixelFormat; 24 | import android.graphics.Rect; 25 | import android.graphics.drawable.Drawable; 26 | import android.support.annotation.Nullable; 27 | 28 | public class SoundLevelCircleDrawable extends Drawable { 29 | public static final int HALO_COLOR_DEF = Color.argb(16, 0x0, 0x0, 0x0); 30 | public static final int CENTER_COLOR_DEF = 0xffF26C29; 31 | private static final float MIN_VALUE = 1f / 2; 32 | private static final float MAX_VALUE = 10; 33 | private static final float INITIAL_VALUE = 2.5f; 34 | 35 | private final float maxRadius; 36 | private final float minRadius; 37 | private final float circleCenterX; 38 | private final float circleCenterY; 39 | private float minMicLevel = MIN_VALUE; 40 | private float maxMicLevel = MAX_VALUE; 41 | 42 | private boolean drawSoundLevel = false; 43 | 44 | private final Paint paintIndicatorHalo; 45 | private final Paint paintIndicatorCenter; 46 | private float smoothedLevel = INITIAL_VALUE; 47 | private final Rect bounds = new Rect(); 48 | private boolean drawCenter = false; 49 | 50 | private static Paint newColorPaint(final int color) { 51 | final Paint paint = new Paint(); 52 | paint.setStyle(Paint.Style.FILL); 53 | paint.setAntiAlias(true); 54 | paint.setColor(color); 55 | return paint; 56 | } 57 | 58 | public SoundLevelCircleDrawable() { 59 | this(null); 60 | } 61 | 62 | public SoundLevelCircleDrawable(@Nullable final Params params) { 63 | final int centerColor; 64 | final int haloColor; 65 | if (params != null) { 66 | this.maxRadius = params.maxRadius; 67 | this.minRadius = params.minRadius; 68 | this.circleCenterX = params.circleCenterX; 69 | this.circleCenterY = params.circleCenterY; 70 | centerColor = params.centerColor; 71 | haloColor = params.haloColor; 72 | } else { 73 | this.maxRadius = -1; 74 | this.minRadius = -1; 75 | this.circleCenterX = -1; 76 | this.circleCenterY = -1; 77 | centerColor = CENTER_COLOR_DEF; 78 | haloColor = HALO_COLOR_DEF; 79 | } 80 | paintIndicatorHalo = newColorPaint(haloColor); 81 | paintIndicatorCenter = newColorPaint(centerColor); 82 | } 83 | 84 | @SuppressWarnings("MagicNumber") 85 | @Override 86 | public void draw(final Canvas canvas) { 87 | if (drawSoundLevel || drawCenter) { 88 | canvas.save(); 89 | try { 90 | if (this.maxRadius < 0 || this.circleCenterX < 0 || this.circleCenterY < 0) 91 | canvas.getClipBounds(bounds); 92 | 93 | canvas.drawColor(Color.TRANSPARENT); 94 | final float levelInFraction = (smoothedLevel - minMicLevel) / (maxMicLevel - minMicLevel); 95 | final float maxRadius = this.maxRadius < 0 ? bounds.width() / 2f : this.maxRadius; 96 | final float minRadius = this.minRadius < 0 ? maxRadius * (65 / 112.5f) : this.minRadius; 97 | final float soundMinRadius = minRadius * 0.8f; //to hide halo on silence 98 | final float rangeRadius = maxRadius - soundMinRadius; 99 | final float soundRadius = soundMinRadius + rangeRadius * levelInFraction; 100 | final float x = this.circleCenterX < 0 ? bounds.width() / 2f : this.circleCenterX; 101 | final float y = this.circleCenterY < 0 ? bounds.height() / 2f : this.circleCenterY; 102 | if (drawSoundLevel) 103 | canvas.drawCircle(x, y, soundRadius, paintIndicatorHalo); 104 | if (drawCenter || drawSoundLevel) 105 | canvas.drawCircle(x, y, minRadius, paintIndicatorCenter); 106 | } finally { 107 | canvas.restore(); 108 | } 109 | } 110 | } 111 | 112 | @Override 113 | public void setAlpha(final int ignored) { 114 | //ignore 115 | } 116 | 117 | @Override 118 | public void setColorFilter(final ColorFilter ignored) { 119 | //ignore 120 | } 121 | 122 | @Override 123 | public int getOpacity() { 124 | return PixelFormat.TRANSLUCENT; 125 | } 126 | 127 | /** 128 | * @param drawSoundLevel true to draw 129 | * @return true if drawSoundLevel changed 130 | */ 131 | public boolean setDrawSoundLevel(final boolean drawSoundLevel) { 132 | if (this.drawSoundLevel != drawSoundLevel) { 133 | this.drawSoundLevel = drawSoundLevel; 134 | if (drawSoundLevel) { 135 | minMicLevel = MIN_VALUE; 136 | maxMicLevel = MAX_VALUE; 137 | smoothedLevel = INITIAL_VALUE; 138 | } 139 | return true; 140 | } else 141 | return false; 142 | } 143 | 144 | public void setDrawCenter(final boolean drawCenter) { 145 | this.drawCenter = drawCenter; 146 | } 147 | 148 | public void setSoundLevel(final float soundLevel) { 149 | final float positiveSoundLevel = Math.abs(soundLevel); 150 | 151 | if (positiveSoundLevel < minMicLevel) { 152 | minMicLevel = (minMicLevel + positiveSoundLevel) / 2; // average 153 | } 154 | 155 | if (positiveSoundLevel > maxMicLevel) { 156 | maxMicLevel = (maxMicLevel + positiveSoundLevel) / 2; // average 157 | } 158 | 159 | final float alpha = 0.2f; 160 | smoothedLevel = (smoothedLevel * (1 - alpha) + (positiveSoundLevel * alpha)); 161 | 162 | if (smoothedLevel > maxMicLevel) { 163 | smoothedLevel = maxMicLevel; 164 | } else if (smoothedLevel < minMicLevel) { 165 | smoothedLevel = minMicLevel; 166 | } 167 | } 168 | 169 | public float getMinRadius() { 170 | return minRadius; 171 | } 172 | 173 | public static class Params { 174 | public final float maxRadius; 175 | public final float minRadius; 176 | public final float circleCenterX; 177 | public final float circleCenterY; 178 | private final int centerColor; 179 | private final int haloColor; 180 | 181 | public Params(final float maxRadius, final float minRadius, final float circleCenterX, final float circleCenterY, final int centerColor, final int haloColor) { 182 | this.maxRadius = maxRadius; 183 | this.minRadius = minRadius; 184 | this.circleCenterX = circleCenterX; 185 | this.circleCenterY = circleCenterY; 186 | this.centerColor = centerColor; 187 | this.haloColor = haloColor; 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/util/RecognizerChecker.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.util; 18 | 19 | import android.content.ComponentName; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.pm.PackageInfo; 23 | import android.content.pm.PackageManager; 24 | import android.content.pm.ResolveInfo; 25 | import android.content.pm.ServiceInfo; 26 | import android.os.Build; 27 | import android.speech.RecognitionService; 28 | 29 | import java.util.LinkedList; 30 | import java.util.List; 31 | 32 | public class RecognizerChecker { 33 | 34 | public static final String GOOGLE_RECOGNIZER_PACKAGE_NAME = "com.google.android.googlequicksearchbox"; 35 | public static final String GOOGLE_VOICE_SEARCH_PACKAGE_NAME = "com.google.android.voicesearch"; 36 | 37 | public static ComponentName findGoogleRecognizer(final Context context) { 38 | return findRecognizerByPackage(context, getPackageName()); 39 | } 40 | 41 | private static ComponentName findRecognizerByPackage(final Context context, final String prefPackage) { 42 | final PackageManager pm = context.getPackageManager(); 43 | final List available = pm != null ? pm.queryIntentServices(new Intent(RecognitionService.SERVICE_INTERFACE), 0) : new LinkedList(); 44 | final int numAvailable = available.size(); 45 | 46 | if (numAvailable == 0) { 47 | // no available voice recognition services found 48 | return null; 49 | } else { 50 | if (prefPackage != null) { 51 | for (final ResolveInfo anAvailable : available) { 52 | final ServiceInfo serviceInfo = anAvailable.serviceInfo; 53 | 54 | if (serviceInfo != null && prefPackage.equals(serviceInfo.packageName)) { 55 | return new ComponentName(serviceInfo.packageName, serviceInfo.name); 56 | } 57 | } 58 | } 59 | // Do not pick up first available, but use default one 60 | return null; 61 | } 62 | } 63 | 64 | public static String getGoogleRecognizerVersion(final Context context) { 65 | try { 66 | final PackageManager pm = context.getPackageManager(); 67 | 68 | final ComponentName recognizerComponentName = findGoogleRecognizer(context); 69 | if (recognizerComponentName != null) { 70 | final PackageInfo packageInfo = pm.getPackageInfo(recognizerComponentName.getPackageName(), 0); 71 | final String versionName = packageInfo.versionName; 72 | return versionName; 73 | } 74 | 75 | return ""; 76 | } catch (final PackageManager.NameNotFoundException ignored) { 77 | return ""; 78 | } 79 | } 80 | 81 | public static boolean isGoogleRecognizerAvailable(final Context context) { 82 | return findGoogleRecognizer(context) != null; 83 | //return false; 84 | } 85 | 86 | private static String getPackageName() { 87 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 88 | return GOOGLE_RECOGNIZER_PACKAGE_NAME; 89 | } else { 90 | return GOOGLE_VOICE_SEARCH_PACKAGE_NAME; 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/util/VersionConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.util; 18 | 19 | import android.content.Context; 20 | import android.text.TextUtils; 21 | 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | import java.util.regex.Pattern; 25 | 26 | import ai.api.android.GsonFactory; 27 | 28 | public class VersionConfig { 29 | 30 | private static final String TAG = VersionConfig.class.getName(); 31 | private static final Pattern DOT_PATTERN = Pattern.compile(".", Pattern.LITERAL); 32 | 33 | private static final Map configuration = new HashMap<>(); 34 | 35 | static { 36 | configuration.put("5.9.26", new VersionConfig(true, true)); 37 | configuration.put("4.7.13", new VersionConfig(false, false)); 38 | } 39 | 40 | private boolean destroyRecognizer = true; 41 | private boolean autoStopRecognizer = false; 42 | 43 | private VersionConfig() { 44 | } 45 | 46 | private VersionConfig(final boolean destroyRecognizer, final boolean autoStopRecognizer) { 47 | this.destroyRecognizer = destroyRecognizer; 48 | this.autoStopRecognizer = autoStopRecognizer; 49 | } 50 | 51 | public static VersionConfig init(final Context context) { 52 | return getConfigByVersion(context); 53 | } 54 | 55 | private static VersionConfig getConfigByVersion(final Context context) { 56 | final long number = numberFromBuildVersion(RecognizerChecker.getGoogleRecognizerVersion(context)); 57 | 58 | final VersionConfig config = new VersionConfig(); 59 | long prevVersionNumber = 0; 60 | 61 | for (final Map.Entry configEntry : configuration.entrySet()) { 62 | final String versionName = configEntry.getKey(); 63 | 64 | if (!TextUtils.isEmpty(versionName)) { 65 | final long versionNumber = numberFromBuildVersion(versionName); 66 | if (number >= versionNumber && prevVersionNumber < versionNumber) { 67 | config.destroyRecognizer = configEntry.getValue().destroyRecognizer; 68 | config.autoStopRecognizer = configEntry.getValue().autoStopRecognizer; 69 | prevVersionNumber = versionNumber; 70 | } 71 | } 72 | } 73 | 74 | return config; 75 | } 76 | 77 | public boolean isDestroyRecognizer() { 78 | return destroyRecognizer; 79 | } 80 | 81 | public boolean isAutoStopRecognizer() { 82 | return autoStopRecognizer; 83 | } 84 | 85 | private static long numberFromBuildVersion(final String buildVersion) { 86 | if (TextUtils.isEmpty(buildVersion)) 87 | return 0; 88 | 89 | final String[] parts = DOT_PATTERN.split(buildVersion); 90 | 91 | final StringBuilder builder = new StringBuilder(); 92 | for (int i = 0; i < Math.min(3, parts.length); i++) { 93 | builder.append(parts[i]); 94 | } 95 | try { 96 | return Long.parseLong(builder.toString()); 97 | } catch (final NumberFormatException ignored) { 98 | return 0; 99 | } 100 | } 101 | 102 | @Override 103 | public String toString() { 104 | return GsonFactory.getGson().toJson(this); 105 | } 106 | } -------------------------------------------------------------------------------- /ailib/src/main/java/ai/api/util/VoiceActivityDetector.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.util; 18 | 19 | import android.util.Log; 20 | 21 | import java.nio.ByteBuffer; 22 | import java.nio.ByteOrder; 23 | import java.nio.ShortBuffer; 24 | 25 | public class VoiceActivityDetector { 26 | 27 | public static final String TAG = VoiceActivityDetector.class.getName(); 28 | 29 | public static final int FRAME_SIZE_IN_BYTES = 320; 30 | private static final int SEQUENCE_LENGTH_MILLIS = 30; 31 | private static final int MIN_SPEECH_SEQUENCE_COUNT = 3; 32 | private static final long MIN_SILENCE_MILLIS = 800; 33 | private static final long MAX_SILENCE_MILLIS = 3500; 34 | private static final long SILENCE_DIFF_MILLIS = MAX_SILENCE_MILLIS - MIN_SILENCE_MILLIS; 35 | private static final int NOISE_FRAMES = 15; 36 | public static final int NOISE_BYTES = NOISE_FRAMES * FRAME_SIZE_IN_BYTES; 37 | private static final double ENERGY_FACTOR = 3.1; 38 | private static final int MIN_CZ = 5; 39 | private static final int MAX_CZ = 15; 40 | 41 | private final int sampleRate; 42 | 43 | private SpeechEventsListener eventsListener; 44 | 45 | private double noiseEnergy = 0.0; 46 | 47 | private long lastActiveTime = -1; 48 | 49 | /** 50 | * last time active frame hit sequence. 51 | */ 52 | private long lastSequenceTime = 0; 53 | 54 | /** 55 | * number of active frame in sequence. 56 | */ 57 | private int sequenceCounter = 0; 58 | 59 | /** 60 | * current processed time in millis 61 | */ 62 | private long time = 0; 63 | 64 | private int frameNumber; 65 | 66 | private long silenceMillis = MAX_SILENCE_MILLIS; 67 | 68 | private boolean speechActive = false; 69 | private boolean enabled = true; 70 | private boolean process = true; 71 | 72 | private double sum = 0; 73 | private int size = 0; 74 | 75 | public VoiceActivityDetector(final int sampleRate) { 76 | this.sampleRate = sampleRate; 77 | } 78 | 79 | public void processBuffer(final byte[] buffer, final int bytesRead) { 80 | if (!process) { 81 | return; 82 | } 83 | 84 | final ByteBuffer byteBuffer = ByteBuffer.wrap(buffer, 0, bytesRead).order(ByteOrder.LITTLE_ENDIAN); 85 | final ShortBuffer shorts = byteBuffer.asShortBuffer(); 86 | 87 | final boolean active = isFrameActive(shorts); 88 | 89 | final int frameSize = bytesRead / 2; // 16 bit encoding 90 | time = frameNumber * frameSize * 1000 / sampleRate; 91 | 92 | if (active) { 93 | if (lastActiveTime >= 0 && (time - lastActiveTime) < SEQUENCE_LENGTH_MILLIS) { 94 | if (++sequenceCounter >= MIN_SPEECH_SEQUENCE_COUNT) { 95 | if (!speechActive) { 96 | onSpeechBegin(); 97 | } 98 | 99 | lastSequenceTime = time; 100 | silenceMillis = Math.max(MIN_SILENCE_MILLIS, silenceMillis - SILENCE_DIFF_MILLIS / 4); 101 | } 102 | } else { 103 | sequenceCounter = 1; 104 | } 105 | lastActiveTime = time; 106 | } else { 107 | if (time - lastSequenceTime > silenceMillis) { 108 | if (speechActive) { 109 | onSpeechEnd(); 110 | } else { 111 | onSpeechCancel(); 112 | } 113 | } 114 | } 115 | } 116 | 117 | private boolean isFrameActive(final ShortBuffer frame) { 118 | 119 | int lastSign = 0; 120 | int czCount = 0; 121 | double energy = 0.0; 122 | 123 | final int frameSize = frame.limit(); 124 | size += frameSize; 125 | 126 | for (int i = 0; i < frameSize; i++) { 127 | final short raw = frame.get(i); 128 | final double amplitude = (double) raw / (double) Short.MAX_VALUE; 129 | energy += (float) amplitude * (float) amplitude / (double) frameSize; 130 | 131 | sum += raw * raw; 132 | 133 | final int sign = (float) amplitude > 0 ? 1 : -1; 134 | if (lastSign != 0 && sign != lastSign) { 135 | czCount++; 136 | } 137 | lastSign = sign; 138 | } 139 | 140 | boolean result = false; 141 | if (++frameNumber < NOISE_FRAMES) { 142 | noiseEnergy += (energy / (double) NOISE_FRAMES); 143 | } else { 144 | if (czCount >= MIN_CZ && czCount <= MAX_CZ) { 145 | if (energy > noiseEnergy * ENERGY_FACTOR) { 146 | result = true; 147 | } 148 | } 149 | } 150 | 151 | return result; 152 | } 153 | 154 | public double calculateRms() { 155 | final double rms = Math.sqrt(sum / size) / 100; 156 | sum = 0; 157 | size = 0; 158 | return rms; 159 | } 160 | 161 | public void reset() { 162 | time = 0; 163 | frameNumber = 0; 164 | 165 | noiseEnergy = 0.0; 166 | lastActiveTime = -1; 167 | lastSequenceTime = 0; 168 | sequenceCounter = 0; 169 | silenceMillis = MAX_SILENCE_MILLIS; 170 | 171 | speechActive = false; 172 | process = true; 173 | } 174 | 175 | public void setSpeechListener(final SpeechEventsListener eventsListener) { 176 | this.eventsListener = eventsListener; 177 | } 178 | 179 | private void onSpeechEnd() { 180 | Log.v(TAG, "onSpeechEnd"); 181 | 182 | speechActive = false; 183 | process = false; 184 | 185 | if (enabled) { 186 | if (eventsListener != null) { 187 | eventsListener.onSpeechEnd(); 188 | } 189 | } 190 | } 191 | 192 | private void onSpeechCancel() { 193 | Log.v(TAG, "onSpeechCancel"); 194 | 195 | speechActive = false; 196 | process = false; 197 | 198 | if (eventsListener != null) { 199 | eventsListener.onSpeechCancel(); 200 | } 201 | } 202 | 203 | private void onSpeechBegin() { 204 | Log.v(TAG, "onSpeechBegin"); 205 | 206 | speechActive = true; 207 | 208 | if (eventsListener != null) { 209 | eventsListener.onSpeechBegin(); 210 | } 211 | } 212 | 213 | /** 214 | * If enabled, voice activity detector fires onSpeechEnd events. 215 | * This option does not affect onSpeechBegin and onChangeLevel events 216 | * 217 | * @param enabled new option values 218 | */ 219 | public void setEnabled(final boolean enabled) { 220 | this.enabled = enabled; 221 | } 222 | 223 | /** 224 | * Used to notify about speech begin/end events 225 | */ 226 | public interface SpeechEventsListener { 227 | void onSpeechBegin(); 228 | 229 | void onSpeechCancel(); 230 | 231 | void onSpeechEnd(); 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /ailib/src/main/res/color/mic_colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /ailib/src/main/res/drawable-xxhdpi/cube.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/main/res/drawable-xxhdpi/cube.png -------------------------------------------------------------------------------- /ailib/src/main/res/drawable-xxhdpi/microphone_control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/main/res/drawable-xxhdpi/microphone_control.png -------------------------------------------------------------------------------- /ailib/src/main/res/drawable-xxhdpi/speaker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/main/res/drawable-xxhdpi/speaker.png -------------------------------------------------------------------------------- /ailib/src/main/res/drawable-xxhdpi/speaker_silent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/main/res/drawable-xxhdpi/speaker_silent.png -------------------------------------------------------------------------------- /ailib/src/main/res/drawable/mic_control.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ailib/src/main/res/layout/aidialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 14 | 15 | 22 | 23 | 24 | 25 | 38 | 39 | 51 | 52 | -------------------------------------------------------------------------------- /ailib/src/main/res/raw/version_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "googleApp": [ 3 | { 4 | "version": "5.9.26", 5 | "destroyRecognizer": true 6 | }, 7 | { 8 | "version": "4.7.13", 9 | "destroyRecognizer": false 10 | } 11 | ] 12 | } -------------------------------------------------------------------------------- /ailib/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ailib/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #f26c29 4 | #20000000 5 | #FF2a91be 6 | 7 | #ffffffff 8 | 9 | -------------------------------------------------------------------------------- /ailib/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 152dp 4 | 35dp 5 | 76dp 6 | 7 | -------------------------------------------------------------------------------- /ailib/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AILib 3 | 4 | Speak 5 | 6 | -------------------------------------------------------------------------------- /ailib/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 | 25 | 26 | 33 | 34 | -------------------------------------------------------------------------------- /ailib/src/test/java/ai/api/test/ProtocolProdTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.test; 18 | 19 | import org.junit.runner.RunWith; 20 | import org.robolectric.RobolectricTestRunner; 21 | import org.robolectric.annotation.Config; 22 | 23 | import ai.api.BuildConfig; 24 | 25 | @Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = BuildConfig.TESTS_SDK) 26 | @RunWith(RobolectricTestRunner.class) 27 | public class ProtocolProdTest extends ProtocolTestBase { 28 | 29 | // Testing keys 30 | protected static final String ACCESS_TOKEN = "3485a96fb27744db83e78b8c4bc9e7b7"; 31 | 32 | protected String getAccessToken() { 33 | return ACCESS_TOKEN; 34 | } 35 | 36 | @Override 37 | protected String getSecondAccessToken() { 38 | return "968235e8e4954cf0bb0dc07736725ecd"; 39 | } 40 | 41 | protected String getRuAccessToken(){ 42 | return "07806228a357411d83064309a279c7fd"; 43 | } 44 | 45 | protected String getBrAccessToken(){ 46 | // TODO 47 | return ""; 48 | } 49 | 50 | protected String getPtBrAccessToken(){ 51 | return "42db6ad6a51c47088318a8104833b66c"; 52 | } 53 | 54 | @Override 55 | protected String getJaAccessToken() { 56 | // TODO 57 | return ""; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /ailib/src/test/java/ai/api/test/VADTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.test; 18 | 19 | import org.junit.Test; 20 | import org.junit.runner.RunWith; 21 | import org.robolectric.RobolectricTestRunner; 22 | import org.robolectric.annotation.Config; 23 | 24 | import java.io.InputStream; 25 | 26 | import ai.api.BuildConfig; 27 | import ai.api.util.VoiceActivityDetector; 28 | 29 | import static org.junit.Assert.assertFalse; 30 | import static org.junit.Assert.assertTrue; 31 | 32 | @Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = BuildConfig.TESTS_SDK) 33 | @RunWith(RobolectricTestRunner.class) 34 | public class VADTest { 35 | 36 | private static final int SAMPLE_RATE = 16000; 37 | 38 | boolean voiceDetected = false; 39 | boolean speechStarted = false; 40 | 41 | @Test 42 | public void testSpeechDetect() { 43 | final VoiceActivityDetector vad = new VoiceActivityDetector(SAMPLE_RATE); 44 | 45 | final InputStream inputStream = getClass().getClassLoader().getResourceAsStream("speech.raw"); 46 | 47 | voiceDetected = false; 48 | speechStarted = false; 49 | 50 | vad.setSpeechListener(new VoiceActivityDetector.SpeechEventsListener() { 51 | @Override 52 | public void onSpeechBegin() { 53 | speechStarted = true; 54 | } 55 | 56 | @Override 57 | public void onSpeechCancel() { 58 | 59 | } 60 | 61 | @Override 62 | public void onSpeechEnd() { 63 | voiceDetected = true; 64 | } 65 | }); 66 | 67 | try { 68 | final byte[] frame = new byte[VoiceActivityDetector.FRAME_SIZE_IN_BYTES]; 69 | while (inputStream.read(frame, 0, frame.length) == frame.length) { 70 | vad.processBuffer(frame, frame.length); 71 | } 72 | 73 | assertTrue(speechStarted); 74 | assertTrue(voiceDetected); 75 | 76 | } catch (final Exception e) { 77 | e.printStackTrace(); 78 | assertTrue(e.getMessage(), false); 79 | } 80 | } 81 | 82 | @Test 83 | public void testSilence() { 84 | final VoiceActivityDetector vad = new VoiceActivityDetector(SAMPLE_RATE); 85 | 86 | final InputStream inputStream = getClass().getClassLoader().getResourceAsStream("silence.raw"); 87 | 88 | voiceDetected = false; 89 | 90 | vad.setSpeechListener(new VoiceActivityDetector.SpeechEventsListener() { 91 | @Override 92 | public void onSpeechBegin() { 93 | } 94 | 95 | @Override 96 | public void onSpeechCancel() { 97 | } 98 | 99 | @Override 100 | public void onSpeechEnd() { 101 | voiceDetected = true; 102 | } 103 | 104 | }); 105 | 106 | try { 107 | final byte[] frame = new byte[VoiceActivityDetector.FRAME_SIZE_IN_BYTES]; 108 | while (inputStream.read(frame, 0, frame.length) == frame.length) { 109 | vad.processBuffer(frame, frame.length); 110 | } 111 | 112 | assertFalse(voiceDetected); 113 | 114 | } catch (final Exception e) { 115 | e.printStackTrace(); 116 | assertTrue(e.getMessage(), false); 117 | } 118 | } 119 | 120 | // @Test TODO enable after VAD improvement 121 | public void testNoise() { 122 | final VoiceActivityDetector vad = new VoiceActivityDetector(SAMPLE_RATE); 123 | 124 | final InputStream inputStream = getClass().getClassLoader().getResourceAsStream("noiseOnly.raw"); 125 | 126 | voiceDetected = false; 127 | 128 | vad.setSpeechListener(new VoiceActivityDetector.SpeechEventsListener() { 129 | @Override 130 | public void onSpeechBegin() { 131 | 132 | } 133 | 134 | @Override 135 | public void onSpeechCancel() { 136 | 137 | } 138 | 139 | @Override 140 | public void onSpeechEnd() { 141 | voiceDetected = true; 142 | } 143 | 144 | }); 145 | 146 | try { 147 | final byte[] frame = new byte[VoiceActivityDetector.FRAME_SIZE_IN_BYTES]; 148 | while (inputStream.read(frame, 0, frame.length) == frame.length) { 149 | vad.processBuffer(frame, frame.length); 150 | } 151 | 152 | assertFalse(voiceDetected); 153 | 154 | } catch (final Exception e) { 155 | e.printStackTrace(); 156 | assertTrue(e.getMessage(), false); 157 | } 158 | } 159 | 160 | @Test 161 | public void testEnabled() { 162 | final VoiceActivityDetector vad = new VoiceActivityDetector(SAMPLE_RATE); 163 | vad.setEnabled(false); 164 | 165 | final InputStream inputStream = getClass().getClassLoader().getResourceAsStream("speech.raw"); 166 | 167 | voiceDetected = false; 168 | speechStarted = false; 169 | 170 | vad.setSpeechListener(new VoiceActivityDetector.SpeechEventsListener() { 171 | @Override 172 | public void onSpeechBegin() { 173 | speechStarted = true; 174 | } 175 | 176 | @Override 177 | public void onSpeechCancel() { 178 | 179 | } 180 | 181 | @Override 182 | public void onSpeechEnd() { 183 | voiceDetected = true; 184 | } 185 | 186 | }); 187 | 188 | try { 189 | final byte[] frame = new byte[VoiceActivityDetector.FRAME_SIZE_IN_BYTES]; 190 | while (inputStream.read(frame, 0, frame.length) == frame.length) { 191 | vad.processBuffer(frame, frame.length); 192 | } 193 | 194 | assertTrue(speechStarted); 195 | assertFalse(voiceDetected); 196 | 197 | } catch (final Exception e) { 198 | e.printStackTrace(); 199 | assertTrue(e.getMessage(), false); 200 | } 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /ailib/src/test/java/ai/api/test/compatibility/SimpleProtocolTestingService.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.test.compatibility; 18 | 19 | import android.content.Context; 20 | 21 | import java.io.InputStream; 22 | import java.net.MalformedURLException; 23 | 24 | import ai.api.android.AIConfiguration; 25 | import ai.api.android.AIDataService; 26 | import ai.api.AIServiceException; 27 | 28 | public class SimpleProtocolTestingService extends AIDataService { 29 | public SimpleProtocolTestingService(final Context context, final AIConfiguration config) { 30 | super(context, config); 31 | } 32 | 33 | public String doDefaultProtocolTextRequest(final String requestJson) throws MalformedURLException, AIServiceException { 34 | return doTextRequest(requestJson); 35 | } 36 | 37 | public String doDefaultProtocolSoundRequest(final InputStream voiceStream, final String queryData) throws MalformedURLException, AIServiceException { 38 | return doSoundRequest(voiceStream, queryData); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ailib/src/test/java/ai/api/test/compatibility/V20150415ProtocolTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.test.compatibility; 18 | 19 | import org.junit.Ignore; 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | import org.robolectric.RobolectricTestRunner; 23 | import org.robolectric.RuntimeEnvironment; 24 | import org.robolectric.annotation.Config; 25 | 26 | import java.util.Collections; 27 | import java.util.List; 28 | 29 | import ai.api.android.AIConfiguration; 30 | import ai.api.android.AIDataService; 31 | import ai.api.AIServiceException; 32 | import ai.api.BuildConfig; 33 | import ai.api.model.AIContext; 34 | import ai.api.model.AIRequest; 35 | import ai.api.model.AIResponse; 36 | 37 | import static org.junit.Assert.assertEquals; 38 | 39 | /** 40 | * Test for backward compatibility to the first protocol version ("20150415") 41 | */ 42 | @Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = BuildConfig.TESTS_SDK) 43 | @RunWith(RobolectricTestRunner.class) 44 | @Ignore 45 | public class V20150415ProtocolTest { 46 | private static final String PROTOCOL_VERSION = "20150415"; 47 | 48 | @Test 49 | public void legacyContextsWithoutParametersTest() throws AIServiceException { 50 | final AIConfiguration config = new AIConfiguration( 51 | "3485a96fb27744db83e78b8c4bc9e7b7", 52 | AIConfiguration.SupportedLanguages.English, 53 | AIConfiguration.RecognitionEngine.System); 54 | 55 | config.setProtocolVersion(PROTOCOL_VERSION); 56 | 57 | final AIDataService aiDataService = new AIDataService(RuntimeEnvironment.application, config); 58 | 59 | final AIContext weatherContext = new AIContext("weather"); 60 | weatherContext.setParameters(Collections.singletonMap("location", "London")); 61 | 62 | final List contexts = Collections.singletonList(weatherContext); 63 | 64 | final AIRequest aiRequest = new AIRequest(); 65 | aiRequest.setQuery("and for tomorrow"); 66 | aiRequest.setContexts(contexts); 67 | 68 | final AIResponse aiResponse = aiDataService.request(aiRequest); 69 | 70 | // Old protocol doesn't support parameters, so response will not contains city name 71 | assertEquals("Weather in for tomorrow", aiResponse.getResult().getFulfillment().getSpeech()); 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /ailib/src/test/java/ai/api/test/compatibility/default_protocol_model/AIResponseDefault.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.test.compatibility.default_protocol_model; 18 | 19 | import com.google.gson.annotations.SerializedName; 20 | 21 | import java.io.Serializable; 22 | import java.util.Date; 23 | 24 | import ai.api.model.Status; 25 | 26 | public class AIResponseDefault implements Serializable { 27 | 28 | /** 29 | * Unique identifier of the result. 30 | */ 31 | @SerializedName("id") 32 | private String id; 33 | 34 | @SerializedName("timestamp") 35 | private Date timestamp; 36 | 37 | /** 38 | * Result object 39 | */ 40 | @SerializedName("result") 41 | private ResultDefault result; 42 | 43 | @SerializedName("status") 44 | private Status status; 45 | 46 | /** 47 | * Unique identifier of the result. 48 | */ 49 | public String getId() { 50 | return id; 51 | } 52 | 53 | public void setId(final String id) { 54 | this.id = id; 55 | } 56 | 57 | public Date getTimestamp() { 58 | return timestamp; 59 | } 60 | 61 | public void setTimestamp(final Date timestamp) { 62 | this.timestamp = timestamp; 63 | } 64 | 65 | /** 66 | * Result object 67 | */ 68 | public ResultDefault getResult() { 69 | return result; 70 | } 71 | 72 | public void setResult(final ResultDefault result) { 73 | this.result = result; 74 | } 75 | 76 | public Status getStatus() { 77 | return status; 78 | } 79 | 80 | public void setStatus(final Status status) { 81 | this.status = status; 82 | } 83 | 84 | public boolean isError() { 85 | if (status != null && status.getCode() != null && status.getCode() >= 400) { 86 | return true; 87 | } 88 | 89 | return false; 90 | } 91 | 92 | @Override 93 | public String toString() { 94 | return String.format("AIResponse{id='%s', timestamp=%s, result=%s, status=%s}", 95 | id, 96 | timestamp, 97 | result, 98 | status); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /ailib/src/test/java/ai/api/test/compatibility/default_protocol_model/MetadataDefault.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.test.compatibility.default_protocol_model; 18 | 19 | import com.google.gson.annotations.SerializedName; 20 | 21 | import java.io.Serializable; 22 | 23 | public class MetadataDefault implements Serializable { 24 | 25 | /** 26 | * Name of the intent that produced this result 27 | */ 28 | @SerializedName("intentName") 29 | private String intentName; 30 | 31 | /** 32 | * Id of the intent that produced this result 33 | */ 34 | @SerializedName("intentId") 35 | private String intentId; 36 | 37 | /** 38 | * Currently active contexts 39 | */ 40 | @SerializedName("contexts") 41 | private String[] contexts; 42 | 43 | /** 44 | * Name of the intent that produced this result 45 | */ 46 | public String getIntentName() { 47 | return intentName; 48 | } 49 | 50 | public void setIntentName(final String intentName) { 51 | this.intentName = intentName; 52 | } 53 | 54 | /** 55 | * Id of the intent that produced this result 56 | */ 57 | public String getIntentId() { 58 | return intentId; 59 | } 60 | 61 | public void setIntentId(final String intentId) { 62 | this.intentId = intentId; 63 | } 64 | 65 | public String[] getContexts() { 66 | return contexts; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ailib/src/test/java/ai/api/test/compatibility/default_protocol_model/ResultDefault.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.test.compatibility.default_protocol_model; 18 | 19 | import com.google.gson.JsonElement; 20 | import com.google.gson.annotations.SerializedName; 21 | 22 | import java.io.Serializable; 23 | import java.util.HashMap; 24 | 25 | public class ResultDefault implements Serializable { 26 | 27 | @SerializedName("speech") 28 | private String speech; 29 | 30 | @SerializedName("action") 31 | private String action; 32 | 33 | /** 34 | * This field will be deserialized as hashMap container with all parameters and it's values 35 | */ 36 | @SerializedName("parameters") 37 | private HashMap parameters; 38 | 39 | @SerializedName("metadata") 40 | private MetadataDefault metadata; 41 | 42 | /** 43 | * The query that was used to produce this result 44 | */ 45 | @SerializedName("resolvedQuery") 46 | private String resolvedQuery; 47 | 48 | public String getSpeech() { 49 | return speech; 50 | } 51 | 52 | public void setSpeech(final String speech) { 53 | this.speech = speech; 54 | } 55 | 56 | public String getAction() { 57 | return action; 58 | } 59 | 60 | public void setAction(final String action) { 61 | this.action = action; 62 | } 63 | 64 | public MetadataDefault getMetadata() { 65 | return metadata; 66 | } 67 | 68 | public void setMetadata(final MetadataDefault metadata) { 69 | this.metadata = metadata; 70 | } 71 | 72 | public HashMap getParameters() { 73 | return parameters; 74 | } 75 | 76 | /** 77 | * The query that was used to produce this result 78 | */ 79 | public String getResolvedQuery() { 80 | return resolvedQuery; 81 | } 82 | 83 | public void setResolvedQuery(final String resolvedQuery) { 84 | this.resolvedQuery = resolvedQuery; 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return String.format("Result {speech='%s', action='%s', resolvedQuery='%s'}", 90 | speech, 91 | action, 92 | resolvedQuery); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /ailib/src/test/java/ai/api/test/compatibility/v20150204_protocol_model/AIResponseV20150204.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.test.compatibility.v20150204_protocol_model; 18 | 19 | import com.google.gson.annotations.SerializedName; 20 | 21 | import java.io.Serializable; 22 | import java.util.Date; 23 | 24 | import ai.api.model.Result; 25 | import ai.api.model.Status; 26 | 27 | public class AIResponseV20150204 implements Serializable { 28 | 29 | /** 30 | * Unique identifier of the result. 31 | */ 32 | @SerializedName("id") 33 | private String id; 34 | 35 | @SerializedName("timestamp") 36 | private Date timestamp; 37 | 38 | /** 39 | * Result object 40 | */ 41 | @SerializedName("result") 42 | private ResultV20150204 result; 43 | 44 | @SerializedName("status") 45 | private Status status; 46 | 47 | /** 48 | * Unique identifier of the result. 49 | */ 50 | public String getId() { 51 | return id; 52 | } 53 | 54 | public void setId(final String id) { 55 | this.id = id; 56 | } 57 | 58 | public Date getTimestamp() { 59 | return timestamp; 60 | } 61 | 62 | public void setTimestamp(final Date timestamp) { 63 | this.timestamp = timestamp; 64 | } 65 | 66 | /** 67 | * Result object 68 | */ 69 | public ResultV20150204 getResult() { 70 | return result; 71 | } 72 | 73 | public void setResult(final ResultV20150204 result) { 74 | this.result = result; 75 | } 76 | 77 | public Status getStatus() { 78 | return status; 79 | } 80 | 81 | public void setStatus(final Status status) { 82 | this.status = status; 83 | } 84 | 85 | public boolean isError() { 86 | if (status != null && status.getCode() != null && status.getCode() >= 400) { 87 | return true; 88 | } 89 | 90 | return false; 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return String.format("AIResponse{id='%s', timestamp=%s, result=%s, status=%s}", 96 | id, 97 | timestamp, 98 | result, 99 | status); 100 | } 101 | 102 | public void cleanup() { 103 | if (result != null) { 104 | result.trimParameters(); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /ailib/src/test/java/ai/api/test/compatibility/v20150204_protocol_model/ResultV20150204.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.test.compatibility.v20150204_protocol_model; 18 | 19 | import android.support.annotation.NonNull; 20 | import android.text.TextUtils; 21 | 22 | import com.google.gson.JsonElement; 23 | import com.google.gson.JsonPrimitive; 24 | import com.google.gson.annotations.SerializedName; 25 | 26 | import java.io.Serializable; 27 | import java.text.ParseException; 28 | import java.util.Date; 29 | import java.util.HashMap; 30 | import java.util.LinkedList; 31 | import java.util.List; 32 | 33 | import ai.api.model.AIOutputContext; 34 | import ai.api.model.Fulfillment; 35 | import ai.api.model.Metadata; 36 | import ai.api.util.ParametersConverter; 37 | 38 | public class ResultV20150204 implements Serializable { 39 | 40 | private static final String DATE_FORMAT_ERROR_MESSAGE = "'%s' parameter has value '%s' and can't be parsed as a Date"; 41 | 42 | @SerializedName("speech") 43 | private String speech; 44 | 45 | @SerializedName("action") 46 | private String action; 47 | 48 | /** 49 | * This field will be deserialized as hashMap container with all parameters and it's values 50 | */ 51 | @SerializedName("parameters") 52 | private HashMap parameters; 53 | 54 | /** 55 | * Currently active contexts 56 | */ 57 | @SerializedName("contexts") 58 | private AIOutputContext[] contexts; 59 | 60 | 61 | @SerializedName("metadata") 62 | private Metadata metadata; 63 | 64 | /** 65 | * The query that was used to produce this result 66 | */ 67 | @SerializedName("resolvedQuery") 68 | private String resolvedQuery; 69 | 70 | public String getSpeech() { 71 | return speech; 72 | } 73 | 74 | public void setSpeech(final String speech) { 75 | this.speech = speech; 76 | } 77 | 78 | @NonNull 79 | public String getAction() { 80 | if (action == null) { 81 | return ""; 82 | } 83 | return action; 84 | } 85 | 86 | public void setAction(final String action) { 87 | this.action = action; 88 | } 89 | 90 | public Metadata getMetadata() { 91 | return metadata; 92 | } 93 | 94 | public void setMetadata(final Metadata metadata) { 95 | this.metadata = metadata; 96 | } 97 | 98 | public HashMap getParameters() { 99 | return parameters; 100 | } 101 | 102 | public String getStringParameter(final String name) { 103 | return getStringParameter(name, ""); 104 | } 105 | 106 | public String getStringParameter(final String name, final String defaultValue) { 107 | if (parameters.containsKey(name)) { 108 | final String parameterValue = parameters.get(name).getAsString(); 109 | return parameterValue; 110 | } 111 | return defaultValue; 112 | } 113 | 114 | public Date getDateParameter(final String name) throws ParseException { 115 | return getDateParameter(name, null); 116 | } 117 | 118 | public Date getDateParameter(final String name, final Date defaultValue) throws IllegalArgumentException { 119 | if (parameters.containsKey(name)) { 120 | final String parameterStringValue = parameters.get(name).getAsString(); 121 | 122 | if (TextUtils.isEmpty(parameterStringValue)) { 123 | return defaultValue; 124 | } 125 | 126 | try { 127 | return ParametersConverter.parseDate(parameterStringValue); 128 | } catch (final ParseException pe) { 129 | throw new IllegalArgumentException(String.format(DATE_FORMAT_ERROR_MESSAGE, name, parameterStringValue), pe); 130 | } 131 | 132 | } 133 | return defaultValue; 134 | } 135 | 136 | public Date getDateTimeParameter(final String name) throws ParseException { 137 | return getDateTimeParameter(name, null); 138 | } 139 | 140 | public Date getDateTimeParameter(final String name, final Date defaultValue) throws IllegalArgumentException { 141 | if (parameters.containsKey(name)) { 142 | final String parameterStringValue = parameters.get(name).getAsString(); 143 | 144 | if (TextUtils.isEmpty(parameterStringValue)) { 145 | return defaultValue; 146 | } 147 | 148 | try { 149 | return ParametersConverter.parseDateTime(parameterStringValue); 150 | } catch (final ParseException pe) { 151 | throw new IllegalArgumentException(String.format(DATE_FORMAT_ERROR_MESSAGE, name, parameterStringValue), pe); 152 | } 153 | 154 | } 155 | return defaultValue; 156 | } 157 | 158 | public Date getTimeParameter(final String name) throws ParseException { 159 | return getTimeParameter(name, null); 160 | } 161 | 162 | public Date getTimeParameter(final String name, final Date defaultValue) throws IllegalArgumentException { 163 | if (parameters.containsKey(name)) { 164 | final String parameterStringValue = parameters.get(name).getAsString(); 165 | 166 | if (TextUtils.isEmpty(parameterStringValue)) { 167 | return defaultValue; 168 | } 169 | 170 | try { 171 | return ParametersConverter.parseTime(parameterStringValue); 172 | } catch (final ParseException pe) { 173 | throw new IllegalArgumentException(String.format(DATE_FORMAT_ERROR_MESSAGE, name, parameterStringValue), pe); 174 | } 175 | 176 | } 177 | return defaultValue; 178 | } 179 | 180 | public int getIntParameter(final String name) { 181 | return getIntParameter(name, 0); 182 | } 183 | 184 | public int getIntParameter(final String name, final int defaultValue) { 185 | if (parameters.containsKey(name)) { 186 | final String parameterStringValue = parameters.get(name).getAsString(); 187 | 188 | if (TextUtils.isEmpty(parameterStringValue)) { 189 | return defaultValue; 190 | } 191 | 192 | return ParametersConverter.parseInteger(parameterStringValue); 193 | } 194 | return defaultValue; 195 | } 196 | 197 | public float getFloatParameter(final String name) { 198 | return getFloatParameter(name, 0); 199 | } 200 | 201 | public float getFloatParameter(final String name, final float defaultValue) { 202 | if (parameters.containsKey(name)) { 203 | final String parameterStringValue = parameters.get(name).getAsString(); 204 | 205 | if (TextUtils.isEmpty(parameterStringValue)) { 206 | return defaultValue; 207 | } 208 | 209 | return ParametersConverter.parseFloat(parameterStringValue); 210 | } 211 | return defaultValue; 212 | } 213 | 214 | public AIOutputContext[] getContexts() { 215 | return contexts; 216 | } 217 | 218 | /** 219 | * The query that was used to produce this result 220 | */ 221 | public String getResolvedQuery() { 222 | return resolvedQuery; 223 | } 224 | 225 | public void setResolvedQuery(final String resolvedQuery) { 226 | this.resolvedQuery = resolvedQuery; 227 | } 228 | 229 | void trimParameters() { 230 | if (parameters != null) { 231 | final List parametersToTrim = new LinkedList(); 232 | for (final String key : parameters.keySet()) { 233 | final JsonElement jsonElement = parameters.get(key); 234 | if (jsonElement != null && jsonElement.isJsonPrimitive()) { 235 | if (((JsonPrimitive) jsonElement).isString() && TextUtils.isEmpty(jsonElement.getAsString())) { 236 | parametersToTrim.add(key); 237 | } 238 | } 239 | } 240 | for (final String key : parametersToTrim) { 241 | parameters.remove(key); 242 | } 243 | } 244 | } 245 | 246 | @Override 247 | public String toString() { 248 | return String.format("Result {speech='%s', action='%s', resolvedQuery='%s'}", 249 | speech, 250 | action, 251 | resolvedQuery); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /ailib/src/test/resources/debug0.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/test/resources/debug0.wav -------------------------------------------------------------------------------- /ailib/src/test/resources/log.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/test/resources/log.raw -------------------------------------------------------------------------------- /ailib/src/test/resources/noiseAndNotification.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/test/resources/noiseAndNotification.raw -------------------------------------------------------------------------------- /ailib/src/test/resources/noiseOnly.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/test/resources/noiseOnly.raw -------------------------------------------------------------------------------- /ailib/src/test/resources/silence.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/test/resources/silence.raw -------------------------------------------------------------------------------- /ailib/src/test/resources/speech.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/test/resources/speech.raw -------------------------------------------------------------------------------- /ailib/src/test/resources/what_is_your_name.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/ailib/src/test/resources/what_is_your_name.raw -------------------------------------------------------------------------------- /apiAISampleApp/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.neenbedankt.android-apt' 3 | 4 | android { 5 | compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) 6 | buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION 7 | 8 | defaultConfig { 9 | applicationId 'ai.api.sample' 10 | minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) 11 | targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) 12 | versionCode Integer.parseInt(project.VERSION_CODE) 13 | versionName project.VERSION_NAME 14 | } 15 | buildTypes { 16 | release { 17 | //proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | productFlavors { 21 | } 22 | 23 | packagingOptions { 24 | } 25 | } 26 | 27 | repositories { 28 | mavenLocal() 29 | mavenCentral() 30 | 31 | maven { url "https://oss.sonatype.org/content/repositories/snapshots" } 32 | } 33 | 34 | dependencies { 35 | compile fileTree(dir: 'libs', include: ['*.jar']) 36 | compile 'com.android.support:appcompat-v7:23.2.1' 37 | // compile 'ai.api:sdk:2.0.7@aar' 38 | // this line is for testing purposes 39 | compile project(':ailib') 40 | } 41 | -------------------------------------------------------------------------------- /apiAISampleApp/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/xvir/sdk/android/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 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 35 | 36 | 37 | 41 | 44 | 45 | 46 | 50 | 53 | 54 | 55 | 59 | 62 | 63 | 64 | 68 | 69 | 70 | 71 | 75 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/AIApplication.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.app.Application; 20 | import android.content.Context; 21 | import android.util.Log; 22 | 23 | import ai.api.util.BluetoothController; 24 | 25 | public class AIApplication extends Application { 26 | 27 | private static final String TAG = AIApplication.class.getSimpleName(); 28 | 29 | private int activitiesCount; 30 | private BluetoothControllerImpl bluetoothController; 31 | private SettingsManager settingsManager; 32 | 33 | @Override 34 | public void onCreate() { 35 | super.onCreate(); 36 | bluetoothController = new BluetoothControllerImpl(this); 37 | settingsManager = new SettingsManager(this); 38 | } 39 | 40 | public BluetoothController getBluetoothController() { 41 | return bluetoothController; 42 | } 43 | 44 | public SettingsManager getSettingsManager() { 45 | return settingsManager; 46 | } 47 | 48 | protected void onActivityResume() { 49 | if (activitiesCount++ == 0) { // on become foreground 50 | if (settingsManager.isUseBluetooth()) { 51 | bluetoothController.start(); 52 | } 53 | } 54 | } 55 | 56 | protected void onActivityPaused() { 57 | if (--activitiesCount == 0) { // on become background 58 | bluetoothController.stop(); 59 | } 60 | } 61 | 62 | private boolean isInForeground() { 63 | return activitiesCount > 0; 64 | } 65 | 66 | private class BluetoothControllerImpl extends BluetoothController { 67 | 68 | public BluetoothControllerImpl(Context context) { 69 | super(context); 70 | } 71 | 72 | @Override 73 | public void onHeadsetDisconnected() { 74 | Log.d(TAG, "Bluetooth headset disconnected"); 75 | } 76 | 77 | @Override 78 | public void onHeadsetConnected() { 79 | Log.d(TAG, "Bluetooth headset connected"); 80 | 81 | if (isInForeground() && settingsManager.isUseBluetooth() 82 | && !bluetoothController.isOnHeadsetSco()) { 83 | bluetoothController.start(); 84 | } 85 | } 86 | 87 | @Override 88 | public void onScoAudioDisconnected() { 89 | Log.d(TAG, "Bluetooth sco audio finished"); 90 | bluetoothController.stop(); 91 | 92 | if (isInForeground() && settingsManager.isUseBluetooth()) { 93 | bluetoothController.start(); 94 | } 95 | } 96 | 97 | @Override 98 | public void onScoAudioConnected() { 99 | Log.d(TAG, "Bluetooth sco audio started"); 100 | } 101 | 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/AIButtonSampleActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.content.Intent; 20 | import android.os.Bundle; 21 | import android.util.Log; 22 | import android.view.Menu; 23 | import android.view.MenuItem; 24 | import android.widget.TextView; 25 | 26 | import com.google.gson.Gson; 27 | import com.google.gson.JsonElement; 28 | 29 | import java.util.HashMap; 30 | import java.util.Map; 31 | 32 | import ai.api.android.AIConfiguration; 33 | import ai.api.android.GsonFactory; 34 | import ai.api.model.AIError; 35 | import ai.api.model.AIResponse; 36 | import ai.api.model.Metadata; 37 | import ai.api.model.Result; 38 | import ai.api.model.Status; 39 | import ai.api.ui.AIButton; 40 | 41 | public class AIButtonSampleActivity extends BaseActivity implements AIButton.AIButtonListener { 42 | 43 | public static final String TAG = AIButtonSampleActivity.class.getName(); 44 | 45 | private AIButton aiButton; 46 | private TextView resultTextView; 47 | 48 | private Gson gson = GsonFactory.getGson(); 49 | 50 | @Override 51 | protected void onCreate(final Bundle savedInstanceState) { 52 | super.onCreate(savedInstanceState); 53 | setContentView(R.layout.activity_aibutton_sample); 54 | 55 | resultTextView = (TextView) findViewById(R.id.resultTextView); 56 | aiButton = (AIButton) findViewById(R.id.micButton); 57 | 58 | final AIConfiguration config = new AIConfiguration(Config.ACCESS_TOKEN, 59 | AIConfiguration.SupportedLanguages.English, 60 | AIConfiguration.RecognitionEngine.System); 61 | 62 | config.setRecognizerStartSound(getResources().openRawResourceFd(R.raw.test_start)); 63 | config.setRecognizerStopSound(getResources().openRawResourceFd(R.raw.test_stop)); 64 | config.setRecognizerCancelSound(getResources().openRawResourceFd(R.raw.test_cancel)); 65 | 66 | aiButton.initialize(config); 67 | aiButton.setResultsListener(this); 68 | } 69 | 70 | @Override 71 | protected void onPause() { 72 | super.onPause(); 73 | 74 | // use this method to disconnect from speech recognition service 75 | // Not destroying the SpeechRecognition object in onPause method would block other apps from using SpeechRecognition service 76 | aiButton.pause(); 77 | } 78 | 79 | @Override 80 | protected void onResume() { 81 | super.onResume(); 82 | 83 | // use this method to reinit connection to recognition service 84 | aiButton.resume(); 85 | } 86 | 87 | @Override 88 | public boolean onCreateOptionsMenu(final Menu menu) { 89 | getMenuInflater().inflate(R.menu.menu_aibutton_sample, menu); 90 | return true; 91 | } 92 | 93 | @Override 94 | public boolean onOptionsItemSelected(final MenuItem item) { 95 | final int id = item.getItemId(); 96 | 97 | //noinspection SimplifiableIfStatement 98 | if (id == R.id.action_settings) { 99 | startActivity(AISettingsActivity.class); 100 | return true; 101 | } 102 | return super.onOptionsItemSelected(item); 103 | } 104 | 105 | @Override 106 | public void onResult(final AIResponse response) { 107 | runOnUiThread(new Runnable() { 108 | @Override 109 | public void run() { 110 | Log.d(TAG, "onResult"); 111 | 112 | resultTextView.setText(gson.toJson(response)); 113 | 114 | Log.i(TAG, "Received success response"); 115 | 116 | // this is example how to get different parts of result object 117 | final Status status = response.getStatus(); 118 | Log.i(TAG, "Status code: " + status.getCode()); 119 | Log.i(TAG, "Status type: " + status.getErrorType()); 120 | 121 | final Result result = response.getResult(); 122 | Log.i(TAG, "Resolved query: " + result.getResolvedQuery()); 123 | 124 | Log.i(TAG, "Action: " + result.getAction()); 125 | final String speech = result.getFulfillment().getSpeech(); 126 | Log.i(TAG, "Speech: " + speech); 127 | TTS.speak(speech); 128 | 129 | final Metadata metadata = result.getMetadata(); 130 | if (metadata != null) { 131 | Log.i(TAG, "Intent id: " + metadata.getIntentId()); 132 | Log.i(TAG, "Intent name: " + metadata.getIntentName()); 133 | } 134 | 135 | final HashMap params = result.getParameters(); 136 | if (params != null && !params.isEmpty()) { 137 | Log.i(TAG, "Parameters: "); 138 | for (final Map.Entry entry : params.entrySet()) { 139 | Log.i(TAG, String.format("%s: %s", entry.getKey(), entry.getValue().toString())); 140 | } 141 | } 142 | } 143 | 144 | }); 145 | } 146 | 147 | @Override 148 | public void onError(final AIError error) { 149 | runOnUiThread(new Runnable() { 150 | @Override 151 | public void run() { 152 | Log.d(TAG, "onError"); 153 | resultTextView.setText(error.toString()); 154 | } 155 | }); 156 | } 157 | 158 | @Override 159 | public void onCancelled() { 160 | runOnUiThread(new Runnable() { 161 | @Override 162 | public void run() { 163 | Log.d(TAG, "onCancelled"); 164 | resultTextView.setText(""); 165 | } 166 | }); 167 | } 168 | 169 | private void startActivity(Class cls) { 170 | final Intent intent = new Intent(this, cls); 171 | startActivity(intent); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/AIDialogSampleActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.content.Intent; 20 | import android.os.Bundle; 21 | import android.util.Log; 22 | import android.view.Menu; 23 | import android.view.MenuItem; 24 | import android.view.View; 25 | import android.widget.TextView; 26 | 27 | import com.google.gson.Gson; 28 | import com.google.gson.JsonElement; 29 | 30 | import java.util.HashMap; 31 | import java.util.Map; 32 | 33 | import ai.api.android.AIConfiguration; 34 | import ai.api.android.GsonFactory; 35 | import ai.api.model.AIError; 36 | import ai.api.model.AIResponse; 37 | import ai.api.model.Metadata; 38 | import ai.api.model.Result; 39 | import ai.api.model.Status; 40 | import ai.api.ui.AIDialog; 41 | 42 | public class AIDialogSampleActivity extends BaseActivity implements AIDialog.AIDialogListener { 43 | 44 | private static final String TAG = AIDialogSampleActivity.class.getName(); 45 | 46 | private TextView resultTextView; 47 | private AIDialog aiDialog; 48 | 49 | private Gson gson = GsonFactory.getGson(); 50 | 51 | @Override 52 | public void onCreate(final Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | setContentView(R.layout.activity_aidialog_sample); 55 | 56 | resultTextView = (TextView) findViewById(R.id.resultTextView); 57 | 58 | final AIConfiguration config = new AIConfiguration(Config.ACCESS_TOKEN, 59 | AIConfiguration.SupportedLanguages.English, 60 | AIConfiguration.RecognitionEngine.System); 61 | 62 | aiDialog = new AIDialog(this, config); 63 | aiDialog.setResultsListener(this); 64 | } 65 | 66 | @Override 67 | public void onResult(final AIResponse response) { 68 | runOnUiThread(new Runnable() { 69 | @Override 70 | public void run() { 71 | Log.d(TAG, "onResult"); 72 | 73 | resultTextView.setText(gson.toJson(response)); 74 | 75 | Log.i(TAG, "Received success response"); 76 | 77 | // this is example how to get different parts of result object 78 | final Status status = response.getStatus(); 79 | Log.i(TAG, "Status code: " + status.getCode()); 80 | Log.i(TAG, "Status type: " + status.getErrorType()); 81 | 82 | final Result result = response.getResult(); 83 | Log.i(TAG, "Resolved query: " + result.getResolvedQuery()); 84 | 85 | Log.i(TAG, "Action: " + result.getAction()); 86 | final String speech = result.getFulfillment().getSpeech(); 87 | Log.i(TAG, "Speech: " + speech); 88 | TTS.speak(speech); 89 | 90 | final Metadata metadata = result.getMetadata(); 91 | if (metadata != null) { 92 | Log.i(TAG, "Intent id: " + metadata.getIntentId()); 93 | Log.i(TAG, "Intent name: " + metadata.getIntentName()); 94 | } 95 | 96 | final HashMap params = result.getParameters(); 97 | if (params != null && !params.isEmpty()) { 98 | Log.i(TAG, "Parameters: "); 99 | for (final Map.Entry entry : params.entrySet()) { 100 | Log.i(TAG, String.format("%s: %s", entry.getKey(), entry.getValue().toString())); 101 | } 102 | } 103 | } 104 | 105 | }); 106 | } 107 | 108 | @Override 109 | public void onError(final AIError error) { 110 | runOnUiThread(new Runnable() { 111 | @Override 112 | public void run() { 113 | resultTextView.setText(error.toString()); 114 | } 115 | }); 116 | } 117 | 118 | @Override 119 | public void onCancelled() { 120 | runOnUiThread(new Runnable() { 121 | @Override 122 | public void run() { 123 | resultTextView.setText(""); 124 | } 125 | }); 126 | } 127 | 128 | @Override 129 | protected void onPause() { 130 | if (aiDialog != null) { 131 | aiDialog.pause(); 132 | } 133 | super.onPause(); 134 | } 135 | 136 | @Override 137 | protected void onResume() { 138 | if (aiDialog != null) { 139 | aiDialog.resume(); 140 | } 141 | super.onResume(); 142 | } 143 | 144 | public void buttonListenOnClick(final View view) { 145 | aiDialog.showAndListen(); 146 | } 147 | 148 | 149 | 150 | @Override 151 | public boolean onCreateOptionsMenu(final Menu menu) { 152 | getMenuInflater().inflate(R.menu.menu_aibutton_sample, menu); 153 | return true; 154 | } 155 | 156 | @Override 157 | public boolean onOptionsItemSelected(final MenuItem item) { 158 | final int id = item.getItemId(); 159 | 160 | //noinspection SimplifiableIfStatement 161 | if (id == R.id.action_settings) { 162 | startActivity(AISettingsActivity.class); 163 | return true; 164 | } 165 | return super.onOptionsItemSelected(item); 166 | } 167 | 168 | private void startActivity(Class cls) { 169 | final Intent intent = new Intent(this, cls); 170 | startActivity(intent); 171 | } 172 | 173 | 174 | } 175 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/AISampleAppWidget.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.app.PendingIntent; 20 | import android.appwidget.AppWidgetManager; 21 | import android.appwidget.AppWidgetProvider; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.widget.RemoteViews; 25 | import android.widget.Toast; 26 | 27 | 28 | /** 29 | * Implementation of App Widget functionality. 30 | */ 31 | public class AISampleAppWidget extends AppWidgetProvider { 32 | private static final String ACTION_KEY = "tap_on_icon"; 33 | @Override 34 | public void onReceive(final Context context, final Intent intent) { 35 | super.onReceive(context, intent); 36 | if (intent.getAction().equals(ACTION_KEY)) { 37 | 38 | final Intent popUpIntent = new Intent(context, AIWidgetActivity.class); 39 | 40 | popUpIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 41 | 42 | context.startActivity(popUpIntent); 43 | Toast.makeText(context.getApplicationContext(), "API.AI Dialog shown",Toast.LENGTH_SHORT).show(); 44 | 45 | } 46 | } 47 | 48 | @Override 49 | public void onUpdate(final Context context, final AppWidgetManager appWidgetManager, final int[] appWidgetIds) { 50 | // There may be multiple widgets active, so update all of them 51 | final int N = appWidgetIds.length; 52 | for (int i = 0; i < N; i++) { 53 | updateAppWidget(context, appWidgetManager, appWidgetIds[i]); 54 | } 55 | } 56 | 57 | 58 | @Override 59 | public void onEnabled(final Context context) { 60 | // Enter relevant functionality for when the first widget is created 61 | } 62 | 63 | @Override 64 | public void onDisabled(final Context context) { 65 | // Enter relevant functionality for when the last widget is disabled 66 | } 67 | 68 | static void updateAppWidget(final Context context, final AppWidgetManager appWidgetManager, 69 | final int appWidgetId) { 70 | final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.ai_app_widget); 71 | //views.setTextViewText(R.id.appwidget_text, widgetText); 72 | final Intent intent = new Intent(context, AISampleAppWidget.class); 73 | intent.setAction(ACTION_KEY); 74 | final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); 75 | views.setOnClickPendingIntent(R.id.appwidget_new_app_widget, pendingIntent); 76 | // Instruct the widget manager to update the widget 77 | appWidgetManager.updateAppWidget(appWidgetId, views); 78 | } 79 | } 80 | 81 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/AIServiceSampleActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.content.Intent; 20 | import android.os.Bundle; 21 | import android.text.TextUtils; 22 | import android.util.Log; 23 | import android.view.Menu; 24 | import android.view.MenuItem; 25 | import android.view.View; 26 | import android.widget.AdapterView; 27 | import android.widget.ArrayAdapter; 28 | import android.widget.EditText; 29 | import android.widget.ImageView; 30 | import android.widget.ProgressBar; 31 | import android.widget.Spinner; 32 | import android.widget.TextView; 33 | 34 | import com.google.gson.Gson; 35 | import com.google.gson.JsonElement; 36 | 37 | import java.util.Collections; 38 | import java.util.HashMap; 39 | import java.util.List; 40 | import java.util.Map; 41 | 42 | import ai.api.android.AIConfiguration; 43 | import ai.api.AIListener; 44 | import ai.api.android.AIService; 45 | import ai.api.android.GsonFactory; 46 | import ai.api.RequestExtras; 47 | import ai.api.model.AIContext; 48 | import ai.api.model.AIError; 49 | import ai.api.model.AIResponse; 50 | import ai.api.model.Metadata; 51 | import ai.api.model.Result; 52 | import ai.api.model.Status; 53 | 54 | public class AIServiceSampleActivity extends BaseActivity 55 | implements AIListener, AdapterView.OnItemSelectedListener { 56 | 57 | public static final String TAG = AIServiceSampleActivity.class.getName(); 58 | 59 | 60 | private AIService aiService; 61 | private ProgressBar progressBar; 62 | private ImageView recIndicator; 63 | private TextView resultTextView; 64 | private EditText contextEditText; 65 | 66 | private Gson gson = GsonFactory.getGson(); 67 | 68 | @Override 69 | protected void onCreate(final Bundle savedInstanceState) { 70 | super.onCreate(savedInstanceState); 71 | setContentView(R.layout.activity_aiservice_sample); 72 | 73 | progressBar = (ProgressBar) findViewById(R.id.progressBar); 74 | recIndicator = (ImageView) findViewById(R.id.recIndicator); 75 | resultTextView = (TextView) findViewById(R.id.resultTextView); 76 | contextEditText = (EditText) findViewById(R.id.contextEditText); 77 | 78 | Spinner spinner = (Spinner) findViewById(R.id.selectLanguageSpinner); 79 | final ArrayAdapter languagesAdapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, Config.languages); 80 | spinner.setAdapter(languagesAdapter); 81 | spinner.setOnItemSelectedListener(this); 82 | } 83 | 84 | private void initService(final LanguageConfig selectedLanguage) { 85 | final AIConfiguration.SupportedLanguages lang = AIConfiguration.SupportedLanguages.fromLanguageTag(selectedLanguage.getLanguageCode()); 86 | final AIConfiguration config = new AIConfiguration(selectedLanguage.getAccessToken(), 87 | lang, 88 | AIConfiguration.RecognitionEngine.System); 89 | 90 | if (aiService != null) { 91 | aiService.pause(); 92 | } 93 | 94 | aiService = AIService.getService(this, config); 95 | aiService.setListener(this); 96 | } 97 | 98 | 99 | @Override 100 | public boolean onCreateOptionsMenu(final Menu menu) { 101 | getMenuInflater().inflate(R.menu.menu_aiservice_sample, menu); 102 | return true; 103 | } 104 | 105 | @Override 106 | public boolean onOptionsItemSelected(final MenuItem item) { 107 | final int id = item.getItemId(); 108 | 109 | //noinspection SimplifiableIfStatement 110 | if (id == R.id.action_settings) { 111 | startActivity(AISettingsActivity.class); 112 | return true; 113 | } 114 | return super.onOptionsItemSelected(item); 115 | } 116 | 117 | @Override 118 | protected void onPause() { 119 | super.onPause(); 120 | 121 | // use this method to disconnect from speech recognition service 122 | // Not destroying the SpeechRecognition object in onPause method would block other apps from using SpeechRecognition service 123 | if (aiService != null) { 124 | aiService.pause(); 125 | } 126 | } 127 | 128 | @Override 129 | protected void onResume() { 130 | super.onResume(); 131 | 132 | // use this method to reinit connection to recognition service 133 | if (aiService != null) { 134 | aiService.resume(); 135 | } 136 | } 137 | 138 | public void startRecognition(final View view) { 139 | final String contextString = String.valueOf(contextEditText.getText()); 140 | if (TextUtils.isEmpty(contextString)) { 141 | aiService.startListening(); 142 | } else { 143 | final List contexts = Collections.singletonList(new AIContext(contextString)); 144 | final RequestExtras requestExtras = new RequestExtras(contexts, null); 145 | aiService.startListening(requestExtras); 146 | } 147 | 148 | } 149 | 150 | public void stopRecognition(final View view) { 151 | aiService.stopListening(); 152 | } 153 | 154 | public void cancelRecognition(final View view) { 155 | aiService.cancel(); 156 | } 157 | 158 | @Override 159 | public void onResult(final AIResponse response) { 160 | runOnUiThread(new Runnable() { 161 | @Override 162 | public void run() { 163 | Log.d(TAG, "onResult"); 164 | 165 | resultTextView.setText(gson.toJson(response)); 166 | 167 | Log.i(TAG, "Received success response"); 168 | 169 | // this is example how to get different parts of result object 170 | final Status status = response.getStatus(); 171 | Log.i(TAG, "Status code: " + status.getCode()); 172 | Log.i(TAG, "Status type: " + status.getErrorType()); 173 | 174 | final Result result = response.getResult(); 175 | Log.i(TAG, "Resolved query: " + result.getResolvedQuery()); 176 | 177 | Log.i(TAG, "Action: " + result.getAction()); 178 | 179 | final String speech = result.getFulfillment().getSpeech(); 180 | Log.i(TAG, "Speech: " + speech); 181 | TTS.speak(speech); 182 | 183 | final Metadata metadata = result.getMetadata(); 184 | if (metadata != null) { 185 | Log.i(TAG, "Intent id: " + metadata.getIntentId()); 186 | Log.i(TAG, "Intent name: " + metadata.getIntentName()); 187 | } 188 | 189 | final HashMap params = result.getParameters(); 190 | if (params != null && !params.isEmpty()) { 191 | Log.i(TAG, "Parameters: "); 192 | for (final Map.Entry entry : params.entrySet()) { 193 | Log.i(TAG, String.format("%s: %s", entry.getKey(), entry.getValue().toString())); 194 | } 195 | } 196 | } 197 | 198 | }); 199 | } 200 | 201 | @Override 202 | public void onError(final AIError error) { 203 | runOnUiThread(new Runnable() { 204 | @Override 205 | public void run() { 206 | resultTextView.setText(error.toString()); 207 | } 208 | }); 209 | } 210 | 211 | @Override 212 | public void onAudioLevel(final float level) { 213 | runOnUiThread(new Runnable() { 214 | @Override 215 | public void run() { 216 | float positiveLevel = Math.abs(level); 217 | 218 | if (positiveLevel > 100) { 219 | positiveLevel = 100; 220 | } 221 | progressBar.setProgress((int) positiveLevel); 222 | } 223 | }); 224 | } 225 | 226 | @Override 227 | public void onListeningStarted() { 228 | runOnUiThread(new Runnable() { 229 | @Override 230 | public void run() { 231 | recIndicator.setVisibility(View.VISIBLE); 232 | } 233 | }); 234 | } 235 | 236 | @Override 237 | public void onListeningCanceled() { 238 | runOnUiThread(new Runnable() { 239 | @Override 240 | public void run() { 241 | recIndicator.setVisibility(View.INVISIBLE); 242 | resultTextView.setText(""); 243 | } 244 | }); 245 | } 246 | 247 | @Override 248 | public void onListeningFinished() { 249 | runOnUiThread(new Runnable() { 250 | @Override 251 | public void run() { 252 | recIndicator.setVisibility(View.INVISIBLE); 253 | } 254 | }); 255 | } 256 | 257 | @Override 258 | public void onItemSelected(AdapterView parent, View view, int position, long id) { 259 | final LanguageConfig selectedLanguage = (LanguageConfig) parent.getItemAtPosition(position); 260 | initService(selectedLanguage); 261 | } 262 | 263 | @Override 264 | public void onNothingSelected(AdapterView parent) { 265 | 266 | } 267 | 268 | private void startActivity(Class cls) { 269 | final Intent intent = new Intent(this, cls); 270 | startActivity(intent); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/AISettingsActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.os.Bundle; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | import android.widget.CheckBox; 23 | import android.widget.CompoundButton; 24 | 25 | public class AISettingsActivity extends BaseActivity implements 26 | View.OnClickListener, 27 | CompoundButton.OnCheckedChangeListener { 28 | 29 | private CheckBox bluetoothSwitch; 30 | 31 | private SettingsManager settingsManager; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_settings); 37 | 38 | settingsManager = ((AIApplication) getApplication()).getSettingsManager(); 39 | 40 | ViewGroup bluetoothSection = (ViewGroup) findViewById(R.id.activity_settings_bluetooth_section); 41 | bluetoothSection.setOnClickListener(this); 42 | 43 | bluetoothSwitch = (CheckBox) findViewById(R.id.activity_settings_bluetooth_swith); 44 | bluetoothSwitch.setChecked(settingsManager.isUseBluetooth()); 45 | bluetoothSwitch.setOnCheckedChangeListener(this); 46 | 47 | } 48 | 49 | @Override 50 | public void onClick(View v) { 51 | switch (v.getId()) { 52 | case R.id.activity_settings_bluetooth_section: 53 | bluetoothSwitch.performClick(); 54 | break; 55 | } 56 | } 57 | 58 | @Override 59 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 60 | switch (buttonView.getId()) { 61 | case R.id.activity_settings_bluetooth_swith: 62 | settingsManager.setUseBluetooth(isChecked); 63 | break; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/AIWidgetActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | import android.content.DialogInterface; 19 | import android.os.Bundle; 20 | import android.support.v7.app.ActionBarActivity; 21 | import android.view.Menu; 22 | import android.view.MenuItem; 23 | import android.view.Window; 24 | import android.widget.Toast; 25 | 26 | import ai.api.android.AIConfiguration; 27 | import ai.api.model.AIError; 28 | import ai.api.model.AIResponse; 29 | import ai.api.ui.AIDialog; 30 | 31 | 32 | public class AIWidgetActivity extends ActionBarActivity { 33 | 34 | private AIDialog aiDialog; 35 | 36 | @Override 37 | protected void onCreate(final Bundle savedInstanceState) { 38 | supportRequestWindowFeature(Window.FEATURE_NO_TITLE); 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.activity_widget_sample); 41 | final AIConfiguration config = new AIConfiguration(Config.ACCESS_TOKEN, 42 | AIConfiguration.SupportedLanguages.English, 43 | AIConfiguration.RecognitionEngine.System); 44 | 45 | aiDialog = new AIDialog(this, config); 46 | aiDialog.setResultsListener(new AIDialog.AIDialogListener() { 47 | @Override 48 | public void onResult(final AIResponse aiResponse) { 49 | // TODO Process aiResponse 50 | aiDialog.close(); 51 | Toast.makeText(getApplicationContext(), String.format("%s %s","Successful response: ", 52 | aiResponse.getResult().getResolvedQuery()), Toast.LENGTH_SHORT).show(); 53 | AIWidgetActivity.this.finish(); 54 | } 55 | 56 | @Override 57 | public void onError(final AIError aiError) { 58 | // TODO show error message 59 | aiDialog.close(); 60 | Toast.makeText(getApplicationContext(), aiError.getMessage(), Toast.LENGTH_SHORT).show(); 61 | AIWidgetActivity.this.finish(); 62 | } 63 | 64 | @Override 65 | public void onCancelled() { 66 | aiDialog.close(); 67 | Toast.makeText(getApplicationContext(), "Process cancelled", Toast.LENGTH_SHORT).show(); 68 | AIWidgetActivity.this.finish(); 69 | } 70 | 71 | }); 72 | 73 | aiDialog.getDialog().setOnDismissListener(new DialogInterface.OnDismissListener() { 74 | @Override 75 | public void onDismiss(final DialogInterface dialog) { 76 | Toast.makeText(getApplicationContext(), "Dialog dismissed by user", Toast.LENGTH_SHORT).show(); 77 | AIWidgetActivity.this.finish(); 78 | } 79 | }); 80 | 81 | 82 | } 83 | 84 | @Override 85 | protected void onResume() { 86 | super.onResume(); 87 | aiDialog.showAndListen(); 88 | } 89 | 90 | @Override 91 | public boolean onCreateOptionsMenu(final Menu menu) { 92 | return true; 93 | } 94 | 95 | @Override 96 | public boolean onOptionsItemSelected(final MenuItem item) { 97 | final int id = item.getItemId(); 98 | return super.onOptionsItemSelected(item); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/BaseActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.Manifest; 20 | import android.content.pm.PackageManager; 21 | import android.os.Bundle; 22 | import android.os.Handler; 23 | import android.support.annotation.NonNull; 24 | import android.support.v4.app.ActivityCompat; 25 | import android.support.v4.content.ContextCompat; 26 | import android.support.v7.app.AppCompatActivity; 27 | 28 | public class BaseActivity extends AppCompatActivity { 29 | 30 | private AIApplication app; 31 | 32 | private static final long PAUSE_CALLBACK_DELAY = 500; 33 | private static final int REQUEST_AUDIO_PERMISSIONS_ID = 33; 34 | 35 | private final Handler handler = new Handler(); 36 | private Runnable pauseCallback = new Runnable() { 37 | @Override 38 | public void run() { 39 | app.onActivityPaused(); 40 | } 41 | }; 42 | 43 | @Override 44 | protected void onCreate(Bundle savedInstanceState) { 45 | super.onCreate(savedInstanceState); 46 | app = (AIApplication) getApplication(); 47 | } 48 | 49 | @Override 50 | protected void onResume() { 51 | super.onResume(); 52 | app.onActivityResume(); 53 | } 54 | 55 | @Override 56 | protected void onPause() { 57 | super.onPause(); 58 | handler.postDelayed(pauseCallback, PAUSE_CALLBACK_DELAY); 59 | } 60 | 61 | protected void checkAudioRecordPermission() { 62 | if (ContextCompat.checkSelfPermission(this, 63 | Manifest.permission.RECORD_AUDIO) 64 | != PackageManager.PERMISSION_GRANTED) { 65 | 66 | // Should we show an explanation? 67 | if (ActivityCompat.shouldShowRequestPermissionRationale(this, 68 | Manifest.permission.RECORD_AUDIO)) { 69 | 70 | // Show an explanation to the user *asynchronously* -- don't block 71 | // this thread waiting for the user's response! After the user 72 | // sees the explanation, try again to request the permission. 73 | 74 | } else { 75 | 76 | // No explanation needed, we can request the permission. 77 | 78 | ActivityCompat.requestPermissions(this, 79 | new String[]{Manifest.permission.RECORD_AUDIO}, 80 | REQUEST_AUDIO_PERMISSIONS_ID); 81 | 82 | } 83 | } 84 | } 85 | 86 | @Override 87 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 88 | switch (requestCode) { 89 | case REQUEST_AUDIO_PERMISSIONS_ID: { 90 | // If request is cancelled, the result arrays are empty. 91 | if (grantResults.length > 0 92 | && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 93 | 94 | // permission was granted, yay! Do the 95 | // contacts-related task you need to do. 96 | 97 | } else { 98 | 99 | // permission denied, boo! Disable the 100 | // functionality that depends on this permission. 101 | } 102 | return; 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/Config.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | public abstract class Config { 20 | // copy this keys from your developer dashboard 21 | public static final String ACCESS_TOKEN = "3485a96fb27744db83e78b8c4bc9e7b7"; 22 | 23 | public static final LanguageConfig[] languages = new LanguageConfig[]{ 24 | new LanguageConfig("en", "a11ea1d839e3446d84e402cb97cdadfb"), 25 | new LanguageConfig("ru", "c8acebfbeeaa42ccb986e30573509055"), 26 | new LanguageConfig("de", "ae2afb2dfd3f4a02bb0da9dd32b78ff6"), 27 | new LanguageConfig("pt", "b27372e24ee44db48df4dccbd57ea021"), 28 | new LanguageConfig("pt-BR", "a4e08b5bc87a41098237e3f23a5e1351"), 29 | new LanguageConfig("es", "49be4c10b6a543dfb41d49d88731bd49"), 30 | new LanguageConfig("fr", "62161233bc094a75b3acfe16aeeed203"), 31 | new LanguageConfig("it", "57f80c9c9a2b4e0eae1739349a46e342"), 32 | new LanguageConfig("ja", "b92617a3f82e4b52b3db44436d2d4b8b"), 33 | new LanguageConfig("ko", "447a595234d74561a76b669a88ab3d99"), 34 | new LanguageConfig("zh-CN", "52d2b2bd992749409fc3a7d0605c3db4"), 35 | new LanguageConfig("zh-HK", "760c7a5efe5d43b9a90d62f73251de6a"), 36 | new LanguageConfig("zh-TW", "9cadea114425436cbaeaa504ea56555b"), 37 | }; 38 | 39 | public static final String[] events = new String[]{ 40 | "hello_event", 41 | "goodbye_event", 42 | "how_are_you_event" 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/LanguageConfig.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import java.lang.String; 20 | 21 | public class LanguageConfig { 22 | private final String languageCode; 23 | private final String accessToken; 24 | 25 | public LanguageConfig(final String languageCode, final String accessToken) { 26 | this.languageCode = languageCode; 27 | this.accessToken = accessToken; 28 | } 29 | 30 | public String getLanguageCode() { 31 | return languageCode; 32 | } 33 | 34 | public String getAccessToken() { 35 | return accessToken; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return languageCode; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.content.Intent; 20 | import android.os.Bundle; 21 | import android.view.Menu; 22 | import android.view.MenuItem; 23 | import android.view.View; 24 | 25 | public class MainActivity extends BaseActivity { 26 | 27 | public static final String TAG = MainActivity.class.getName(); 28 | 29 | @Override 30 | protected void onCreate(final Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_main); 33 | 34 | TTS.init(getApplicationContext()); 35 | } 36 | 37 | @Override 38 | protected void onStart() { 39 | super.onStart(); 40 | 41 | checkAudioRecordPermission(); 42 | } 43 | 44 | @Override 45 | public boolean onCreateOptionsMenu(final Menu menu) { 46 | // Inflate the menu; this adds items to the action bar if it is present. 47 | getMenuInflater().inflate(R.menu.main, menu); 48 | return true; 49 | } 50 | 51 | @Override 52 | public boolean onOptionsItemSelected(final MenuItem item) { 53 | // Handle action bar item clicks here. The action bar will 54 | // automatically handle clicks on the Home/Up button, so long 55 | // as you specify a parent activity in AndroidManifest.xml. 56 | final int id = item.getItemId(); 57 | if (id == R.id.action_settings) { 58 | startActivity(AISettingsActivity.class); 59 | return true; 60 | } 61 | return super.onOptionsItemSelected(item); 62 | } 63 | 64 | public void serviceSampleClick(final View view) { 65 | startActivity(AIServiceSampleActivity.class); 66 | } 67 | 68 | public void buttonSampleClick(final View view) { 69 | startActivity(AIButtonSampleActivity.class); 70 | } 71 | 72 | public void dialogSampleClick(final View view) { 73 | startActivity(AIDialogSampleActivity.class); 74 | } 75 | 76 | public void textSampleClick(final View view) { 77 | startActivity(AITextSampleActivity.class); 78 | } 79 | 80 | private void startActivity(Class cls) { 81 | final Intent intent = new Intent(this, cls); 82 | startActivity(intent); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/SettingsManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.content.Context; 20 | import android.content.SharedPreferences; 21 | 22 | import ai.api.util.BluetoothController; 23 | 24 | public class SettingsManager { 25 | 26 | private static final String SETTINGS_PREFS_NAME = "ai.api.APP_SETTINGS"; 27 | private static final String PREF_USE_BLUETOOTH = "USE_BLUETOOTH"; 28 | 29 | private final Context context; 30 | private SharedPreferences prefs; 31 | 32 | private boolean useBluetooth; 33 | 34 | public SettingsManager(final Context context) { 35 | this.context = context; 36 | prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE); 37 | 38 | useBluetooth = prefs.getBoolean(PREF_USE_BLUETOOTH, true); 39 | } 40 | 41 | public void setUseBluetooth(final boolean useBluetooth) { 42 | this.useBluetooth = useBluetooth; 43 | 44 | prefs.edit().putBoolean(PREF_USE_BLUETOOTH, useBluetooth).commit(); 45 | final BluetoothController controller = ((AIApplication) context.getApplicationContext()).getBluetoothController(); 46 | if (useBluetooth) { 47 | controller.start(); 48 | } else { 49 | controller.stop(); 50 | } 51 | } 52 | 53 | public boolean isUseBluetooth() { 54 | return useBluetooth; 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/java/ai/api/sample/TTS.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017 Google Inc. All Rights Reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package ai.api.sample; 18 | 19 | import android.content.Context; 20 | import android.speech.tts.TextToSpeech; 21 | 22 | public class TTS { 23 | 24 | private static TextToSpeech textToSpeech; 25 | 26 | public static void init(final Context context) { 27 | if (textToSpeech == null) { 28 | textToSpeech = new TextToSpeech(context, new TextToSpeech.OnInitListener() { 29 | @Override 30 | public void onInit(int i) { 31 | 32 | } 33 | }); 34 | } 35 | } 36 | 37 | public static void speak(final String text) { 38 | textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/apiAISampleApp/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /apiAISampleApp/src/main/res/drawable-hdpi/recording_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/apiAISampleApp/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /apiAISampleApp/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/apiAISampleApp/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /apiAISampleApp/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dialogflow/dialogflow-android-client/331f3ae8f2e404e245cc6024fdf44ec99feba7ee/apiAISampleApp/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /apiAISampleApp/src/main/res/drawable/view_alpha_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/res/layout/activity_aibutton_sample.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | 17 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /apiAISampleApp/src/main/res/layout/activity_aidialog_sample.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 11 |