diff --git a/Speech2Speech/.gitignore b/Speech2Speech/.gitignore
new file mode 100644
index 00000000..2b75303a
--- /dev/null
+++ b/Speech2Speech/.gitignore
@@ -0,0 +1,13 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Speech2Speech/app/.gitignore b/Speech2Speech/app/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/Speech2Speech/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Speech2Speech/app/build.gradle b/Speech2Speech/app/build.gradle
new file mode 100644
index 00000000..e30b643e
--- /dev/null
+++ b/Speech2Speech/app/build.gradle
@@ -0,0 +1,59 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.example.translate"
+ minSdkVersion 26
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ packagingOptions {
+ exclude 'META-INF/DEPENDENCIES'
+ exclude 'META-INF/LICENSE'
+ exclude 'META-INF/LICENSE.txt'
+ exclude 'META-INF/license.txt'
+ exclude 'META-INF/NOTICE'
+ exclude 'META-INF/NOTICE.txt'
+ exclude 'META-INF/notice.txt'
+ exclude 'META-INF/ASL2.0'
+ exclude 'META-INF/INDEX.LIST'
+ exclude 'META-INF/io.netty.versions.properties'
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'com.android.support:design:28.0.0'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.2.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+ implementation 'com.google.firebase:firebase-auth:18.1.0'
+ implementation 'com.firebaseui:firebase-ui-auth:4.1.0'
+ implementation 'com.google.firebase:firebase-messaging:19.0.1'
+ implementation 'com.google.firebase:firebase-functions:18.1.0'
+
+ implementation 'com.google.cloud:google-cloud-translate:1.87.0'
+ implementation group: 'com.google.cloud', name: 'google-cloud-texttospeech', version: '0.45.0-beta'
+ implementation group: 'com.google.cloud', name: 'google-cloud-speech', version: '1.17.0'
+
+ implementation 'com.android.volley:volley:1.1.1'
+ implementation 'com.google.cloud:google-cloud-dialogflow:0.98.0-alpha'
+ implementation group: 'io.grpc', name: 'grpc-okhttp', version: '1.21.0'
+ implementation group: 'io.grpc', name: 'grpc-netty', version: '1.21.0'
+}
+
+apply plugin: 'com.google.gms.google-services'
diff --git a/Speech2Speech/app/gradle/wrapper/gradle-wrapper.jar b/Speech2Speech/app/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..f6b961fd
Binary files /dev/null and b/Speech2Speech/app/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Speech2Speech/app/gradle/wrapper/gradle-wrapper.properties b/Speech2Speech/app/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..b8d30e27
--- /dev/null
+++ b/Speech2Speech/app/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun Aug 25 22:35:10 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/Speech2Speech/app/gradlew b/Speech2Speech/app/gradlew
new file mode 100644
index 00000000..cccdd3d5
--- /dev/null
+++ b/Speech2Speech/app/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/Speech2Speech/app/gradlew.bat b/Speech2Speech/app/gradlew.bat
new file mode 100644
index 00000000..e95643d6
--- /dev/null
+++ b/Speech2Speech/app/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Speech2Speech/app/proguard-rules.pro b/Speech2Speech/app/proguard-rules.pro
new file mode 100644
index 00000000..f1b42451
--- /dev/null
+++ b/Speech2Speech/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/Speech2Speech/app/src/androidTest/java/com/example/translate/ExampleInstrumentedTest.java b/Speech2Speech/app/src/androidTest/java/com/example/translate/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..95f840e9
--- /dev/null
+++ b/Speech2Speech/app/src/androidTest/java/com/example/translate/ExampleInstrumentedTest.java
@@ -0,0 +1,27 @@
+package com.example.translate;
+
+import android.content.Context;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.example.translate", appContext.getPackageName());
+ }
+}
diff --git a/Speech2Speech/app/src/main/AndroidManifest.xml b/Speech2Speech/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..7475fc2a
--- /dev/null
+++ b/Speech2Speech/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/AppController.java b/Speech2Speech/app/src/main/java/com/example/translate/AppController.java
new file mode 100644
index 00000000..7e6d9229
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/AppController.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.translate;
+
+import android.app.Application;
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.os.Environment;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class AppController extends Application {
+
+ public static final String TOKEN_RECEIVED = "TOKEN_RECEIVED";
+ public static final String SESSION_ID = "sessionId";
+ public static String PROJECT_ID = "";
+ public static Context context;
+
+ public static void playAudio(byte[] byteArray) {
+ MediaPlayer mediaPlayer = new MediaPlayer();
+ try {
+ File tempFile = File.createTempFile("dialogFlow", null, Environment.getExternalStorageDirectory());
+ tempFile.deleteOnExit();
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ fos.write(byteArray);
+ fos.close();
+ mediaPlayer.reset();
+ FileInputStream fis = new FileInputStream(tempFile);
+ mediaPlayer.setDataSource(fis.getFD());
+
+ mediaPlayer.prepare();
+ mediaPlayer.start();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ context = getApplicationContext();
+ PROJECT_ID = getApplicationContext().getString(R.string.gcp_project_id);
+ }
+
+}
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/adapter/ChatRecyclerViewAdapter.java b/Speech2Speech/app/src/main/java/com/example/translate/adapter/ChatRecyclerViewAdapter.java
new file mode 100644
index 00000000..1d140304
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/adapter/ChatRecyclerViewAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.translate.adapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.example.translate.R;
+import com.example.translate.model.ChatMsgModel;
+
+import java.util.ArrayList;
+
+public class ChatRecyclerViewAdapter extends RecyclerView.Adapter {
+
+ private ArrayList chatMsgModels;
+
+ public ChatRecyclerViewAdapter(ArrayList chatMsgModels) {
+ this.chatMsgModels = chatMsgModels;
+
+ }
+
+ @Override
+ public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View itemView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.custom_chat_recyclerview_item, parent, false);
+
+ return new MyViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(MyViewHolder holder, final int position) {
+ final ChatMsgModel chatMsgModel = chatMsgModels.get(position);
+
+ if (chatMsgModel.getType() == 1) { // Message Sent
+ holder.tvMsgSent.setText(chatMsgModel.getMsg());
+ holder.tvMsgSent.setVisibility(View.VISIBLE);
+ holder.tvMsgReceived.setVisibility(View.GONE);
+ } else {
+ holder.tvMsgReceived.setText(chatMsgModel.getMsg());
+ holder.tvMsgReceived.setVisibility(View.VISIBLE);
+ holder.tvMsgSent.setVisibility(View.GONE);
+ }
+
+
+ }
+
+ @Override
+ public int getItemCount() {
+ return chatMsgModels.size();
+ }
+
+ public class MyViewHolder extends RecyclerView.ViewHolder {
+ RelativeLayout rlMain;
+ TextView tvMsgSent;
+ TextView tvMsgReceived;
+
+ public MyViewHolder(View view) {
+ super(view);
+ rlMain = view.findViewById(R.id.rlMain);
+ tvMsgSent = view.findViewById(R.id.tvMsgSent);
+ tvMsgReceived = view.findViewById(R.id.tvMsgReceived);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/adapter/LanguageAdapter.java b/Speech2Speech/app/src/main/java/com/example/translate/adapter/LanguageAdapter.java
new file mode 100644
index 00000000..619a1945
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/adapter/LanguageAdapter.java
@@ -0,0 +1,72 @@
+package com.example.translate.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.cloud.translate.Language;
+
+import java.util.ArrayList;
+
+public class LanguageAdapter extends ArrayAdapter {
+
+ private Context context;
+ private int resource;
+ private ArrayList objects;
+
+ public LanguageAdapter(@NonNull Context context, int resource, @NonNull ArrayList objects) {
+ super(context, resource, objects);
+ this.context = context;
+ this.resource = resource;
+ this.objects = objects;
+ }
+
+ @Override
+ public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+
+ Holder holder;
+
+ if (convertView == null) {
+ holder = new Holder();
+ convertView = LayoutInflater.from(context).inflate(resource, null);
+ holder.text1 = convertView.findViewById(android.R.id.text1);
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+ }
+
+ holder.text1.setText(objects.get(position).getName() + " (" + objects.get(position).getCode() + ")");
+
+ return convertView;
+ }
+
+ @NonNull
+ @Override
+ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+
+ Holder holder;
+
+ if (convertView == null) {
+ holder = new Holder();
+ convertView = LayoutInflater.from(context).inflate(resource, null);
+ holder.text1 = convertView.findViewById(android.R.id.text1);
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+ }
+
+ holder.text1.setText(objects.get(position).getName() + " (" + objects.get(position).getCode() + ")");
+
+ return convertView;
+ }
+
+ class Holder {
+ TextView text1;
+ }
+}
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/adapter/VoiceAdapter.java b/Speech2Speech/app/src/main/java/com/example/translate/adapter/VoiceAdapter.java
new file mode 100644
index 00000000..8478e119
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/adapter/VoiceAdapter.java
@@ -0,0 +1,72 @@
+package com.example.translate.adapter;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.cloud.texttospeech.v1beta1.Voice;
+
+import java.util.ArrayList;
+
+public class VoiceAdapter extends ArrayAdapter {
+
+ private Context context;
+ private int resource;
+ private ArrayList objects;
+
+ public VoiceAdapter(@NonNull Context context, int resource, @NonNull ArrayList objects) {
+ super(context, resource, objects);
+ this.context = context;
+ this.resource = resource;
+ this.objects = objects;
+ }
+
+ @Override
+ public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+
+ Holder holder;
+
+ if (convertView == null) {
+ holder = new Holder();
+ convertView = LayoutInflater.from(context).inflate(resource, null);
+ holder.text1 = convertView.findViewById(android.R.id.text1);
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+ }
+
+ holder.text1.setText(objects.get(position).getName() + " (" + objects.get(position).getSsmlGender().name() + ")");
+
+ return convertView;
+ }
+
+ @NonNull
+ @Override
+ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+
+ Holder holder;
+
+ if (convertView == null) {
+ holder = new Holder();
+ convertView = LayoutInflater.from(context).inflate(resource, null);
+ holder.text1 = convertView.findViewById(android.R.id.text1);
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+ }
+
+ holder.text1.setText(objects.get(position).getName() + " (" + objects.get(position).getSsmlGender().name() + ")");
+
+ return convertView;
+ }
+
+ class Holder {
+ TextView text1;
+ }
+}
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/model/ChatMsgModel.java b/Speech2Speech/app/src/main/java/com/example/translate/model/ChatMsgModel.java
new file mode 100644
index 00000000..61a1f960
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/model/ChatMsgModel.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.translate.model;
+
+public class ChatMsgModel {
+
+ private String msg;
+ private int type;
+
+ public ChatMsgModel(String msg, int type) {
+ this.msg = msg;
+ this.type = type;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+}
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/service/MyFirebaseCloudMessagingService.java b/Speech2Speech/app/src/main/java/com/example/translate/service/MyFirebaseCloudMessagingService.java
new file mode 100644
index 00000000..1d08fe19
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/service/MyFirebaseCloudMessagingService.java
@@ -0,0 +1,66 @@
+
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.translate.service;
+
+import android.content.Intent;
+import android.util.Log;
+
+import com.example.translate.AppController;
+import com.example.translate.utils.AuthUtils;
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+public class MyFirebaseCloudMessagingService extends FirebaseMessagingService {
+
+ private static final String TAG = MyFirebaseCloudMessagingService.class.getSimpleName();
+
+ @Override
+ public void onMessageReceived(RemoteMessage remoteMessage) {
+ Log.i("FirebaseMessage", "From: " + remoteMessage.getFrom());
+
+ // Check if message contains a notification payload.
+ if (remoteMessage.getNotification() != null) {
+ Log.i(TAG, "Notification Body: " + remoteMessage.getNotification().getBody());
+ handleNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody());
+ }
+ }
+
+ /**
+ * function to save the token data in the AppController
+ *
+ * @param expiryTime : expiry time received from FCM
+ * @param token : token received from FCM
+ */
+ private void handleNotification(String expiryTime, String token) {
+ try {
+ AuthUtils.expiryTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).parse(expiryTime);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ AuthUtils.token = token;
+
+ Intent intent = new Intent(AppController.TOKEN_RECEIVED);
+ intent.putExtra("type", "token");
+ sendBroadcast(intent);
+ }
+
+}
\ No newline at end of file
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/ui/MainActivity.java b/Speech2Speech/app/src/main/java/com/example/translate/ui/MainActivity.java
new file mode 100644
index 00000000..a55bf5c0
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/ui/MainActivity.java
@@ -0,0 +1,296 @@
+package com.example.translate.ui;
+
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.MediaRecorder;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.example.translate.AppController;
+import com.example.translate.R;
+import com.example.translate.adapter.ChatRecyclerViewAdapter;
+import com.example.translate.model.ChatMsgModel;
+import com.example.translate.utils.ApiRequest;
+import com.example.translate.utils.AuthUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+public class MainActivity extends AppCompatActivity {
+
+ private static ChatRecyclerViewAdapter chatRecyclerViewAdapter;
+ private static ArrayList chatMsgModels;
+ private static RecyclerView rvChats;
+ private ApiRequest apiRequest;
+
+ private EditText etMsg;
+ private ImageButton btnSend;
+ private ImageButton btnMic;
+ private AlertDialog alert;
+ private String message = "";
+ private String fileName = "";
+
+ private String sourceLanguageCode;
+ private String targetLanguageCode;
+ private int ssmlGenderValue;
+
+ /**
+ * Broadcast receiver to hide the progress dialog when token is received
+ */
+ private BroadcastReceiver br = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (alert != null && alert.isShowing()) {
+ alert.dismiss();
+ }
+ if (intent.getStringExtra("type").equals("token")) {
+ if (!TextUtils.isEmpty(fileName)) {
+ sendMsg("", fileName);
+ } else if (!TextUtils.isEmpty(message)) {
+ sendMsg(message, "");
+ } else if (!etMsg.getText().toString().trim().equals("")) {
+ sendMsg(etMsg.getText().toString().trim(), "");
+ }
+ } else if (intent.getStringExtra("type").equals("addMsg")) {
+ addMsg(intent.getStringExtra("message"), intent.getIntExtra("messageType", 0));
+ }
+ }
+ };
+
+ /**
+ * function to scroll the recyclerview at the bottom after each message sent or received
+ */
+ private static void scrollToBottom() {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (chatMsgModels.size() > 0) {
+ rvChats.smoothScrollToPosition(chatMsgModels.size() - 1);
+ }
+ }
+ });
+ }
+
+ /**
+ * function to addMessage in the recyclerview
+ *
+ * @param msg : message to add
+ * @param type : Type of message (sent|received)
+ */
+ public static void addMsg(String msg, int type) {
+ chatMsgModels.add(new ChatMsgModel(msg, type));
+ chatRecyclerViewAdapter.notifyDataSetChanged();
+ scrollToBottom();
+ }
+
+ /**
+ * function to show the progress dialog
+ */
+ private void showProgressDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage("Fetching auth token...");
+ builder.setCancelable(false);
+
+ alert = builder.create();
+ alert.show();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ sourceLanguageCode = getIntent().getStringExtra("sourceLanguageCode");
+ targetLanguageCode = getIntent().getStringExtra("targetLanguageCode");
+ ssmlGenderValue = getIntent().getIntExtra("ssmlGenderValue", 0);
+
+ initViews();
+ setupRecyclerView();
+ initListeners();
+
+ apiRequest = new ApiRequest();
+
+ }
+
+ /**
+ * function to initialize the views
+ */
+ private void initViews() {
+ etMsg = findViewById(R.id.etMsg);
+ btnSend = findViewById(R.id.btnSend);
+ btnMic = findViewById(R.id.btnMic);
+ rvChats = findViewById(R.id.rvChat);
+ }
+
+ /**
+ * function to initialize the recyclerview
+ */
+ private void setupRecyclerView() {
+ rvChats.setLayoutManager(new LinearLayoutManager(this));
+ chatMsgModels = new ArrayList<>();
+
+ chatRecyclerViewAdapter = new ChatRecyclerViewAdapter(chatMsgModels);
+ rvChats.setAdapter(chatRecyclerViewAdapter);
+ }
+
+ /**
+ * function to initialize the onClick listeners
+ */
+ private void initListeners() {
+ btnSend.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ sendMsg(etMsg.getText().toString(), "");
+ scrollToBottom();
+ }
+ });
+
+ btnMic.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ recordAudio("temp.mp3");
+ }
+ });
+ }
+
+
+ private void getNewToken() {
+ showProgressDialog();
+ if (AuthUtils.checkSignIn()) {
+ AuthUtils.callFirebaseFunction();
+ }
+ }
+
+ /**
+ * function to send the message
+ *
+ * @param msg : message sent from user
+ */
+ private void sendMsg(String msg, String fileName) {
+ if (!TextUtils.isEmpty(msg) || !TextUtils.isEmpty(fileName)) {
+ // check if the token is received and expiry time is received and not expired
+ if (AuthUtils.expiryTime != null && !AuthUtils.token.equals("") && AuthUtils.expiryTime.getTime() > System.currentTimeMillis()) {
+ if (!TextUtils.isEmpty(msg)) {
+ addMsg(msg, 1);
+ }
+ new APIRequest(AuthUtils.token, AuthUtils.expiryTime, msg, fileName, sourceLanguageCode, targetLanguageCode, ssmlGenderValue).execute();
+ etMsg.setText("");
+ this.message = "";
+ this.fileName = "";
+ } else {
+ // get new token if expired or not received
+ getNewToken();
+ }
+ } else {
+ Toast.makeText(this, "Please enter or say some message to send.", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ IntentFilter intentFilter = new IntentFilter(AppController.TOKEN_RECEIVED);
+ registerReceiver(br, intentFilter);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (br != null) {
+ try {
+ unregisterReceiver(br);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ private void recordAudio(final String fileName) {
+ final MediaRecorder recorder = new MediaRecorder();
+ ContentValues values = new ContentValues(3);
+ values.put(MediaStore.MediaColumns.TITLE, fileName);
+ recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+ recorder.setAudioSamplingRate(16000);
+ recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
+ recorder.setOutputFile(Environment.getExternalStorageDirectory() + "/" + fileName);
+ try {
+ recorder.prepare();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ final ProgressDialog mProgressDialog = new ProgressDialog(this);
+ mProgressDialog.setTitle("Recording");
+ mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
+ mProgressDialog.setButton("Stop recording", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ mProgressDialog.dismiss();
+ recorder.stop();
+ recorder.release();
+ sendMsg("", Environment.getExternalStorageDirectory() + "/" + fileName);
+ }
+ });
+
+ mProgressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface p1) {
+ recorder.stop();
+ recorder.release();
+ }
+ });
+ recorder.start();
+ mProgressDialog.show();
+ }
+
+ private class APIRequest extends AsyncTask {
+ private String token;
+ private Date expiryTime;
+ private String msg;
+ private String fileName;
+ private String sourceLanguageCode;
+ private String targetLanguageCode;
+ private int ssmlGenderValue;
+
+ public APIRequest(String token, Date expiryTime, String msg, String fileName, String sourceLanguageCode, String targetLanguageCode, int ssmlGenderValue) {
+ this.token = token;
+ this.expiryTime = expiryTime;
+ this.msg = msg;
+ this.fileName = fileName;
+ this.sourceLanguageCode = sourceLanguageCode;
+ this.targetLanguageCode = targetLanguageCode;
+ this.ssmlGenderValue = ssmlGenderValue;
+ }
+
+ @Override
+ protected String doInBackground(Void... voids) {
+ return apiRequest.callAPI(token, expiryTime, msg, fileName, sourceLanguageCode, targetLanguageCode, ssmlGenderValue);
+ }
+
+ @Override
+ protected void onPostExecute(String response) {
+ super.onPostExecute(response);
+ if (response != null) {
+ addMsg(response, 0);
+ }
+ }
+ }
+
+}
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/ui/SettingsActivity.java b/Speech2Speech/app/src/main/java/com/example/translate/ui/SettingsActivity.java
new file mode 100644
index 00000000..9f4ce0de
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/ui/SettingsActivity.java
@@ -0,0 +1,342 @@
+package com.example.translate.ui;
+
+import android.Manifest;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+
+import com.example.translate.AppController;
+import com.example.translate.R;
+import com.example.translate.adapter.LanguageAdapter;
+import com.example.translate.adapter.VoiceAdapter;
+import com.example.translate.utils.ApiRequest;
+import com.example.translate.utils.AuthUtils;
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.android.gms.tasks.Task;
+import com.google.cloud.texttospeech.v1beta1.Voice;
+import com.google.cloud.translate.Language;
+import com.google.firebase.auth.AuthResult;
+import com.google.firebase.iid.InstanceIdResult;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class SettingsActivity extends AppCompatActivity {
+
+
+ private Spinner spTranslateFrom;
+ private Spinner spTranslateTo;
+ private Spinner spTtsType;
+ private Spinner spVoiceType;
+ private Button btnProceed;
+ private AlertDialog alert;
+ private String languageListCode = "";
+ private ApiRequest apiRequest;
+
+ private ArrayList sourceLanguageList = new ArrayList<>();
+ private ArrayList targetLanguageList = new ArrayList<>();
+ private ArrayList voicesList = new ArrayList<>();
+ private ArrayList filteredVoicesList = new ArrayList<>();
+ private ArrayList ttsTypes = new ArrayList<>();
+ private LanguageAdapter sourceLanguageListArrayAdapter;
+ private LanguageAdapter targetLanguageListArrayAdapter;
+ private VoiceAdapter voicesListArrayAdapter;
+ private ArrayAdapter ttsListArrayAdapter;
+
+ /**
+ * Broadcast receiver to hide the progress dialog when token is received
+ */
+ private BroadcastReceiver br = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ alert.dismiss();
+ fetchSupportedLanguages(languageListCode);
+ }
+ };
+
+ private void fetchSupportedLanguages(String sourceLanguageCode) {
+ if (AuthUtils.expiryTime != null && !AuthUtils.token.equals("") && AuthUtils.expiryTime.getTime() > System.currentTimeMillis()) {
+ new LanguageList(AuthUtils.token, AuthUtils.expiryTime, sourceLanguageCode).execute();
+ } else {
+ // get new token if expired or not received
+ languageListCode = sourceLanguageCode;
+ getNewToken();
+ }
+ }
+
+ private void fetchVoiceslist(String targetLanguageCode) {
+ if (AuthUtils.expiryTime != null && !AuthUtils.token.equals("") && AuthUtils.expiryTime.getTime() > System.currentTimeMillis()) {
+ new VoicesList(AuthUtils.token, AuthUtils.expiryTime, targetLanguageCode).execute();
+ } else {
+ // get new token if expired or not received
+ languageListCode = targetLanguageCode;
+ getNewToken();
+ }
+ }
+
+ private void getNewToken() {
+ showProgressDialog();
+ if (AuthUtils.checkSignIn()) {
+ AuthUtils.callFirebaseFunction();
+ }
+ }
+
+ /**
+ * function to show the progress dialog
+ */
+ private void showProgressDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage("Fetching auth token...");
+ builder.setCancelable(false);
+
+ alert = builder.create();
+ alert.show();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_settings);
+
+ if (AppController.PROJECT_ID.equals("GCP_PROJECT_ID")) {
+ Toast.makeText(this, "Please update the GCP_PROJECT_ID in strings.xml", Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ checkPermissions();
+
+ apiRequest = new ApiRequest();
+
+ initViews();
+ initListeners();
+
+
+ AuthUtils.signInAnonymously(this, new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if (task.isSuccessful()) {
+ // Sign in success, update UI with the signed-in user's information
+ AuthUtils.getFirebaseInstanceId(new OnSuccessListener() {
+ @Override
+ public void onSuccess(InstanceIdResult instanceIdResult) {
+ String deviceToken = instanceIdResult.getToken();
+ AuthUtils.firebaseInstanceId = deviceToken;
+ Log.i("fcmId", deviceToken);
+ fetchSupportedLanguages("");
+ }
+ });
+
+ Toast.makeText(SettingsActivity.this, "Sign In was successful",
+ Toast.LENGTH_SHORT).show();
+ } else {
+ // If sign in fails, display a message to the user.
+ Toast.makeText(SettingsActivity.this, "Authentication failed.",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ });
+
+ loadTtsTypes();
+
+ }
+
+ private void loadTtsTypes() {
+ ttsTypes.add("WaveNet");
+ ttsTypes.add("Standard");
+ ttsListArrayAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, ttsTypes);
+ spTtsType.setAdapter(ttsListArrayAdapter);
+
+ filterVoiceTypes();
+ }
+
+ private void loadVoices() {
+ voicesListArrayAdapter = new VoiceAdapter(SettingsActivity.this, android.R.layout.simple_list_item_1, filteredVoicesList);
+ spVoiceType.setAdapter(voicesListArrayAdapter);
+ }
+
+ private void initViews() {
+ spTranslateFrom = findViewById(R.id.spTranslateFrom);
+ spTranslateTo = findViewById(R.id.spTranslateTo);
+ spTtsType = findViewById(R.id.spTtsType);
+ spVoiceType = findViewById(R.id.spVoiceType);
+ btnProceed = findViewById(R.id.btnProceed);
+ }
+
+ private void initListeners() {
+ spTranslateFrom.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ fetchSupportedLanguages(sourceLanguageList.get(position).getCode());
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+ spTranslateTo.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ fetchVoiceslist(targetLanguageList.get(position).getCode());
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+ spTtsType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+
+ filterVoiceTypes();
+
+ loadVoices();
+
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+
+ }
+ });
+
+ btnProceed.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(SettingsActivity.this, MainActivity.class);
+ intent.putExtra("sourceLanguageCode", sourceLanguageList.get(spTranslateFrom.getSelectedItemPosition()).getCode());
+ intent.putExtra("targetLanguageCode", targetLanguageList.get(spTranslateTo.getSelectedItemPosition()).getCode());
+ if (filteredVoicesList.size() > 0) {
+ intent.putExtra("ssmlGenderValue", filteredVoicesList.get(spVoiceType.getSelectedItemPosition()).getSsmlGenderValue());
+ }
+ startActivity(intent);
+ }
+ });
+ }
+
+ private void filterVoiceTypes() {
+ filteredVoicesList = new ArrayList<>();
+
+ for (int i = 0; i < voicesList.size(); i++) {
+ if (voicesList.get(i).getName().toLowerCase().contains(ttsTypes.get(spTtsType.getSelectedItemPosition()).toLowerCase())) {
+ filteredVoicesList.add(voicesList.get(i));
+ }
+ }
+ }
+
+ public void checkPermissions() {
+
+ ArrayList arrPerm = new ArrayList<>();
+ arrPerm.add(Manifest.permission.INTERNET);
+ arrPerm.add(Manifest.permission.RECORD_AUDIO);
+ arrPerm.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ arrPerm.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+
+ if (!arrPerm.isEmpty()) {
+ String[] permissions = new String[arrPerm.size()];
+ permissions = arrPerm.toArray(permissions);
+ ActivityCompat.requestPermissions(this, permissions, 1);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ IntentFilter intentFilter = new IntentFilter(AppController.TOKEN_RECEIVED);
+ registerReceiver(br, intentFilter);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (br != null) {
+ try {
+ unregisterReceiver(br);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ private class LanguageList extends AsyncTask> {
+
+ private String sourceLanguageCode;
+ private String token;
+ private Date expiryTime;
+
+ public LanguageList(String token, Date expiryTime, String sourceLanguageCode) {
+ this.token = token;
+ this.expiryTime = expiryTime;
+ this.sourceLanguageCode = sourceLanguageCode;
+ }
+
+ @Override
+ protected List doInBackground(Void... voids) {
+ return apiRequest.getSupportedLanguages(token, expiryTime, sourceLanguageCode);
+ }
+
+ @Override
+ protected void onPostExecute(List response) {
+ super.onPostExecute(response);
+ if (TextUtils.isEmpty(sourceLanguageCode)) {
+ sourceLanguageList.addAll(response);
+ sourceLanguageListArrayAdapter = new LanguageAdapter(SettingsActivity.this, android.R.layout.simple_list_item_1, sourceLanguageList);
+ spTranslateFrom.setAdapter(sourceLanguageListArrayAdapter);
+ } else {
+ targetLanguageList.addAll(response);
+ targetLanguageListArrayAdapter = new LanguageAdapter(SettingsActivity.this, android.R.layout.simple_list_item_1, targetLanguageList);
+ spTranslateTo.setAdapter(targetLanguageListArrayAdapter);
+ }
+ }
+ }
+
+ private class VoicesList extends AsyncTask> {
+
+ private String sourceLanguageCode;
+ private String token;
+ private Date expiryTime;
+
+ public VoicesList(String token, Date expiryTime, String sourceLanguageCode) {
+ this.token = token;
+ this.expiryTime = expiryTime;
+ this.sourceLanguageCode = sourceLanguageCode;
+ }
+
+ @Override
+ protected List doInBackground(Void... voids) {
+ return apiRequest.listVoices(token, expiryTime, sourceLanguageCode);
+ }
+
+ @Override
+ protected void onPostExecute(List response) {
+ super.onPostExecute(response);
+ if (response != null) {
+ voicesList.addAll(response);
+ filteredVoicesList.addAll(response);
+ loadVoices();
+ }
+ }
+ }
+}
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/utils/ApiRequest.java b/Speech2Speech/app/src/main/java/com/example/translate/utils/ApiRequest.java
new file mode 100644
index 00000000..3e9e52d1
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/utils/ApiRequest.java
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.translate.utils;
+
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.example.translate.AppController;
+import com.google.api.gax.core.FixedCredentialsProvider;
+import com.google.api.gax.rpc.ApiStreamObserver;
+import com.google.api.gax.rpc.BidiStreamingCallable;
+import com.google.auth.Credentials;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.cloud.speech.v1p1beta1.RecognitionConfig;
+import com.google.cloud.speech.v1p1beta1.SpeechClient;
+import com.google.cloud.speech.v1p1beta1.SpeechRecognitionAlternative;
+import com.google.cloud.speech.v1p1beta1.SpeechSettings;
+import com.google.cloud.speech.v1p1beta1.StreamingRecognitionConfig;
+import com.google.cloud.speech.v1p1beta1.StreamingRecognitionResult;
+import com.google.cloud.speech.v1p1beta1.StreamingRecognizeRequest;
+import com.google.cloud.speech.v1p1beta1.StreamingRecognizeResponse;
+import com.google.cloud.texttospeech.v1beta1.AudioConfig;
+import com.google.cloud.texttospeech.v1beta1.AudioEncoding;
+import com.google.cloud.texttospeech.v1beta1.ListVoicesResponse;
+import com.google.cloud.texttospeech.v1beta1.SynthesisInput;
+import com.google.cloud.texttospeech.v1beta1.SynthesizeSpeechResponse;
+import com.google.cloud.texttospeech.v1beta1.TextToSpeechClient;
+import com.google.cloud.texttospeech.v1beta1.TextToSpeechSettings;
+import com.google.cloud.texttospeech.v1beta1.Voice;
+import com.google.cloud.texttospeech.v1beta1.VoiceSelectionParams;
+import com.google.cloud.translate.Language;
+import com.google.cloud.translate.Translate;
+import com.google.cloud.translate.TranslateOptions;
+import com.google.cloud.translate.v3beta1.LocationName;
+import com.google.cloud.translate.v3beta1.TranslateTextRequest;
+import com.google.cloud.translate.v3beta1.TranslateTextResponse;
+import com.google.cloud.translate.v3beta1.TranslationServiceClient;
+import com.google.cloud.translate.v3beta1.TranslationServiceSettings;
+import com.google.common.util.concurrent.SettableFuture;
+import com.google.protobuf.ByteString;
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+public class ApiRequest {
+
+ private String token = null;
+ private Date tokenExpiration = null;
+
+ public ApiRequest() {
+ // Variables needed to retrieve an auth token
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", java.util.Locale.getDefault());
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ public static List listVoices(String token, Date tokenExpiration, String language) {
+
+ try {
+ AccessToken accessToken = new AccessToken(token, tokenExpiration);
+ Credentials credentials = GoogleCredentials.create(accessToken);
+ FixedCredentialsProvider fixedCredentialsProvider = FixedCredentialsProvider.create(credentials);
+ TextToSpeechSettings textToSpeechSettings = TextToSpeechSettings.newBuilder().setCredentialsProvider(fixedCredentialsProvider).build();
+ try (TextToSpeechClient textToSpeechClient = TextToSpeechClient.create(textToSpeechSettings)) {
+
+ ListVoicesResponse listVoicesResponse = textToSpeechClient.listVoices(language);
+ List voices = listVoicesResponse.getVoicesList();
+
+ for (int i = 0; i < voices.size(); i++) {
+ Log.i("voices", voices.get(i).getName());
+ }
+
+ return voices;
+
+
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public List getSupportedLanguages(String token, Date tokenExpiration, String sourceLanguageCode) {
+
+ try {
+ AccessToken accessToken = new AccessToken(token, tokenExpiration);
+ Credentials credentials = GoogleCredentials.create(accessToken);
+ FixedCredentialsProvider fixedCredentialsProvider = FixedCredentialsProvider.create(credentials);
+ Translate translate = TranslateOptions.newBuilder().setProjectId(AppController.PROJECT_ID).setCredentials(fixedCredentialsProvider.getCredentials()).build().getService();
+ List languages;
+
+ if (TextUtils.isEmpty(sourceLanguageCode)) {
+ languages = translate.listSupportedLanguages();
+ } else {
+ languages = translate.listSupportedLanguages(Translate.LanguageListOption.targetLanguage(sourceLanguageCode));
+ }
+
+ for (Language language : languages) {
+ System.out.printf("Name: %s, Code: %s\n", language.getName(), language.getCode());
+ }
+
+ return languages;
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * function to call: detect Intent Sentiment Analysis | Detect Intent With TTS | KnowledgeBase
+ *
+ * @param accessToken : access token received from fcm
+ * @param expiryTime : expiry time received from fcm
+ * @return : response from the server
+ */
+ public String callAPI(String accessToken, Date expiryTime, String text, String fileName, String sourceLanguageCode, String targetLanguageCode, int ssmlGenderValue) {
+ this.token = accessToken;
+ this.tokenExpiration = expiryTime;
+
+ String response = "";
+ if (!TextUtils.isEmpty(fileName)) {
+ String convertedText = speechToText(accessToken, expiryTime, fileName, sourceLanguageCode);
+ addMsg(convertedText);
+ response = translateText(accessToken, expiryTime, convertedText, sourceLanguageCode, targetLanguageCode);
+ } else if (!TextUtils.isEmpty(text)) {
+ response = translateText(accessToken, expiryTime, text, sourceLanguageCode, targetLanguageCode);
+ }
+
+ textToSpeech(response, ssmlGenderValue, targetLanguageCode);
+ return response;
+ }
+
+ private void addMsg(String message) {
+ Intent intent = new Intent(AppController.TOKEN_RECEIVED);
+ intent.putExtra("type", "addMsg");
+ intent.putExtra("message", message);
+ intent.putExtra("messageType", 1);
+ AppController.context.sendBroadcast(intent);
+ }
+
+ /**
+ * function to getting the results from the dialogflow
+ *
+ * @return : response from the server
+ */
+ private String translateText(String token, Date tokenExpiration, String text, String sourceLanguageCode, String targetLanguageCode) {
+ try {
+ AccessToken accessToken = new AccessToken(token, tokenExpiration);
+ Credentials credentials = GoogleCredentials.create(accessToken);
+ FixedCredentialsProvider fixedCredentialsProvider = FixedCredentialsProvider.create(credentials);
+ TranslationServiceSettings translationServiceSettings = TranslationServiceSettings.newBuilder().setCredentialsProvider(fixedCredentialsProvider).build();
+ try (TranslationServiceClient translationServiceClient = TranslationServiceClient.create(translationServiceSettings)) {
+
+ LocationName locationName =
+ LocationName.newBuilder().setProject(AppController.PROJECT_ID).setLocation("global").build();
+
+ TranslateTextRequest translateTextRequest =
+ TranslateTextRequest.newBuilder()
+ .setParent(locationName.toString())
+ .setMimeType("text/plain")
+ .setSourceLanguageCode(sourceLanguageCode)
+ .setTargetLanguageCode(targetLanguageCode)
+ .addContents(text)
+ .build();
+
+ // Call the API
+ TranslateTextResponse response = translationServiceClient.translateText(translateTextRequest);
+ System.out.format(
+ "Translated Text: %s", response.getTranslationsList().get(0).getTranslatedText());
+ return response.getTranslationsList().get(0).getTranslatedText();
+
+ } catch (Exception e) {
+ throw new RuntimeException("Couldn't create client.", e);
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return ex.getMessage();
+ }
+ }
+
+ private void textToSpeech(String text, int ssmlGenderValue, String languageCode) {
+
+ try {
+ AccessToken accessToken = new AccessToken(token, tokenExpiration);
+ Credentials credentials = GoogleCredentials.create(accessToken);
+ FixedCredentialsProvider fixedCredentialsProvider = FixedCredentialsProvider.create(credentials);
+ TextToSpeechSettings textToSpeechSettings = TextToSpeechSettings.newBuilder().setCredentialsProvider(fixedCredentialsProvider).build();
+ try (TextToSpeechClient textToSpeechClient = TextToSpeechClient.create(textToSpeechSettings)) {
+ // Set the text input to be synthesized
+ SynthesisInput input = SynthesisInput.newBuilder()
+ .setText(text)
+ .build();
+
+ // Build the voice request, select the language code ("en-US") and the ssml voice gender
+ // ("neutral")
+ VoiceSelectionParams voice = VoiceSelectionParams.newBuilder()
+ .setLanguageCode(languageCode)
+ .setSsmlGenderValue(ssmlGenderValue)
+ .build();
+
+ // Select the type of audio file you want returned
+ AudioConfig audioConfig = AudioConfig.newBuilder()
+ .setAudioEncoding(AudioEncoding.MP3)
+ .build();
+
+ // Perform the text-to-speech request on the text input with the selected voice parameters and
+ // audio file type
+ SynthesizeSpeechResponse response = textToSpeechClient.synthesizeSpeech(input, voice,
+ audioConfig);
+
+ // Get the audio contents from the response
+ ByteString audioContents = response.getAudioContent();
+ AppController.playAudio(audioContents.toByteArray());
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ public String speechToText(String token, Date tokenExpiration, String fileName, String languageCode) {
+ try {
+ AccessToken accessToken = new AccessToken(token, tokenExpiration);
+ Credentials credentials = GoogleCredentials.create(accessToken);
+ FixedCredentialsProvider fixedCredentialsProvider = FixedCredentialsProvider.create(credentials);
+ SpeechSettings speechSettings =
+ SpeechSettings.newBuilder()
+ .setCredentialsProvider(fixedCredentialsProvider)
+ .build();
+ try (SpeechClient speechClient = SpeechClient.create(speechSettings)) {
+
+ // Reads the audio file into memory
+ Path path = Paths.get(fileName);
+ byte[] data = Files.readAllBytes(path);
+ ByteString audioBytes = ByteString.copyFrom(data);
+
+ // Builds the sync recognize request
+ RecognitionConfig recConfig = RecognitionConfig.newBuilder()
+ .setEncoding(RecognitionConfig.AudioEncoding.MP3)
+ .setSampleRateHertz(16000)
+ .setLanguageCode(languageCode)
+ .setMaxAlternatives(30)
+ .setModel("default")
+ .build();
+
+ StreamingRecognitionConfig config =
+ StreamingRecognitionConfig.newBuilder().setConfig(recConfig).build();
+
+ class ResponseApiStreamingObserver implements ApiStreamObserver {
+ private final SettableFuture> future = SettableFuture.create();
+ private final List messages = new java.util.ArrayList();
+
+ @Override
+ public void onNext(T message) {
+ messages.add(message);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ future.setException(t);
+ }
+
+ @Override
+ public void onCompleted() {
+ future.set(messages);
+ }
+
+ // Returns the SettableFuture object to get received messages / exceptions.
+ public SettableFuture> future() {
+ return future;
+ }
+ }
+
+ ResponseApiStreamingObserver responseObserver =
+ new ResponseApiStreamingObserver<>();
+
+ BidiStreamingCallable callable =
+ speechClient.streamingRecognizeCallable();
+
+ ApiStreamObserver requestObserver =
+ callable.bidiStreamingCall(responseObserver);
+
+ // The first request must **only** contain the audio configuration:
+ requestObserver.onNext(
+ StreamingRecognizeRequest.newBuilder().setStreamingConfig(config).build());
+
+ // Subsequent requests must **only** contain the audio data.
+ requestObserver.onNext(
+ StreamingRecognizeRequest.newBuilder()
+ .setAudioContent(ByteString.copyFrom(data))
+ .build());
+
+ // Mark transmission as completed after sending the data.
+ requestObserver.onCompleted();
+
+ List responses = responseObserver.future().get();
+ String responseText = "";
+ for (StreamingRecognizeResponse response : responses) {
+ // For streaming recognize, the results list has one is_final result (if available) followed
+ // by a number of in-progress results (if iterim_results is true) for subsequent utterances.
+ // Just print the first result here.
+ StreamingRecognitionResult result = response.getResultsList().get(0);
+ // There can be several alternative transcripts for a given chunk of speech. Just use the
+ // first (most likely) one here.
+ SpeechRecognitionAlternative alternative = result.getAlternativesList().get(0);
+ responseText += alternative.getTranscript();
+ }
+
+ return responseText;
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ return "";
+ }
+}
+
+
diff --git a/Speech2Speech/app/src/main/java/com/example/translate/utils/AuthUtils.java b/Speech2Speech/app/src/main/java/com/example/translate/utils/AuthUtils.java
new file mode 100644
index 00000000..e10cfec5
--- /dev/null
+++ b/Speech2Speech/app/src/main/java/com/example/translate/utils/AuthUtils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.translate.utils;
+
+import android.app.Activity;
+
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.firebase.auth.AuthResult;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.functions.FirebaseFunctions;
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.iid.InstanceIdResult;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+public class AuthUtils {
+
+ public static String firebaseInstanceId = "";
+ public static FirebaseAuth firebaseAuth;
+
+ public static String token = "";
+ public static Date expiryTime;
+
+ /**
+ * function to call the firebase function which will send the fcm message containing token and expiry time to the device
+ */
+ public static void callFirebaseFunction() {
+ Map data = new HashMap<>();
+ data.put("deviceID", firebaseInstanceId);
+
+ FirebaseFunctions.getInstance()
+ .getHttpsCallable("getOAuthToken")
+ .call(data);
+ }
+
+ public static void signInAnonymously(final Activity activity, OnCompleteListener onCompleteListener) {
+ firebaseAuth = FirebaseAuth.getInstance();
+ firebaseAuth.signInAnonymously()
+ .addOnCompleteListener(activity, onCompleteListener);
+ }
+
+ /**
+ * function to check the user is logged in
+ *
+ * @return boolean : returns true if user is logged inn
+ */
+ public static boolean checkSignIn() {
+ return firebaseAuth != null && firebaseAuth.getCurrentUser() != null;
+ }
+
+ public static void getFirebaseInstanceId(OnSuccessListener onSuccessListener) {
+ FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(onSuccessListener);
+ }
+
+}
diff --git a/Speech2Speech/app/src/main/res/drawable-hdpi/ic_mic.png b/Speech2Speech/app/src/main/res/drawable-hdpi/ic_mic.png
new file mode 100755
index 00000000..b27f5171
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-hdpi/ic_mic.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-hdpi/ic_send.png b/Speech2Speech/app/src/main/res/drawable-hdpi/ic_send.png
new file mode 100755
index 00000000..324421c6
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-hdpi/ic_send.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-mdpi/ic_mic.png b/Speech2Speech/app/src/main/res/drawable-mdpi/ic_mic.png
new file mode 100755
index 00000000..68102401
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-mdpi/ic_mic.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-mdpi/ic_send.png b/Speech2Speech/app/src/main/res/drawable-mdpi/ic_send.png
new file mode 100755
index 00000000..55475546
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-mdpi/ic_send.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Speech2Speech/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..1f6bb290
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Speech2Speech/app/src/main/res/drawable-xhdpi/ic_mic.png b/Speech2Speech/app/src/main/res/drawable-xhdpi/ic_mic.png
new file mode 100755
index 00000000..41f74c5b
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-xhdpi/ic_mic.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-xhdpi/ic_send.png b/Speech2Speech/app/src/main/res/drawable-xhdpi/ic_send.png
new file mode 100755
index 00000000..14acf57e
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-xhdpi/ic_send.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-xxhdpi/ic_mic.png b/Speech2Speech/app/src/main/res/drawable-xxhdpi/ic_mic.png
new file mode 100755
index 00000000..d5da5bee
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-xxhdpi/ic_mic.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-xxhdpi/ic_send.png b/Speech2Speech/app/src/main/res/drawable-xxhdpi/ic_send.png
new file mode 100755
index 00000000..6c50f9e8
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-xxhdpi/ic_send.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-xxxhdpi/ic_mic.png b/Speech2Speech/app/src/main/res/drawable-xxxhdpi/ic_mic.png
new file mode 100755
index 00000000..51419b34
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-xxxhdpi/ic_mic.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-xxxhdpi/ic_send.png b/Speech2Speech/app/src/main/res/drawable-xxxhdpi/ic_send.png
new file mode 100755
index 00000000..a081b468
Binary files /dev/null and b/Speech2Speech/app/src/main/res/drawable-xxxhdpi/ic_send.png differ
diff --git a/Speech2Speech/app/src/main/res/drawable-xxxhdpi/rounded_corner.xml b/Speech2Speech/app/src/main/res/drawable-xxxhdpi/rounded_corner.xml
new file mode 100644
index 00000000..982884bd
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/drawable-xxxhdpi/rounded_corner.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speech2Speech/app/src/main/res/drawable/ic_launcher_background.xml b/Speech2Speech/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..0d025f9b
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Speech2Speech/app/src/main/res/layout/activity_main.xml b/Speech2Speech/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..20e71532
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speech2Speech/app/src/main/res/layout/activity_settings.xml b/Speech2Speech/app/src/main/res/layout/activity_settings.xml
new file mode 100644
index 00000000..db78ac9a
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/layout/activity_settings.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speech2Speech/app/src/main/res/layout/custom_chat_recyclerview_item.xml b/Speech2Speech/app/src/main/res/layout/custom_chat_recyclerview_item.xml
new file mode 100644
index 00000000..70d3a8af
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/layout/custom_chat_recyclerview_item.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Speech2Speech/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Speech2Speech/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Speech2Speech/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Speech2Speech/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Speech2Speech/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Speech2Speech/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..898f3ed5
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Speech2Speech/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/Speech2Speech/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..dffca360
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/Speech2Speech/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Speech2Speech/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..64ba76f7
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Speech2Speech/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/Speech2Speech/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..dae5e082
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/Speech2Speech/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Speech2Speech/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..e5ed4659
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Speech2Speech/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/Speech2Speech/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..14ed0af3
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/Speech2Speech/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Speech2Speech/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..b0907cac
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Speech2Speech/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/Speech2Speech/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..d8ae0315
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/Speech2Speech/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Speech2Speech/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..2c18de9e
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Speech2Speech/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/Speech2Speech/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..beed3cdd
Binary files /dev/null and b/Speech2Speech/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/Speech2Speech/app/src/main/res/values/colors.xml b/Speech2Speech/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..69b22338
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #008577
+ #00574B
+ #D81B60
+
diff --git a/Speech2Speech/app/src/main/res/values/strings.xml b/Speech2Speech/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..b836eb45
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+ Translate
+ GCP_PROJECT_ID
+
diff --git a/Speech2Speech/app/src/main/res/values/styles.xml b/Speech2Speech/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..5885930d
--- /dev/null
+++ b/Speech2Speech/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Speech2Speech/app/src/test/java/com/example/translate/ExampleUnitTest.java b/Speech2Speech/app/src/test/java/com/example/translate/ExampleUnitTest.java
new file mode 100644
index 00000000..f60af2d3
--- /dev/null
+++ b/Speech2Speech/app/src/test/java/com/example/translate/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.example.translate;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Speech2Speech/build.gradle b/Speech2Speech/build.gradle
new file mode 100644
index 00000000..9e65d1f4
--- /dev/null
+++ b/Speech2Speech/build.gradle
@@ -0,0 +1,28 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.4.1'
+ classpath 'com.google.gms:google-services:4.2.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Speech2Speech/gradle.properties b/Speech2Speech/gradle.properties
new file mode 100644
index 00000000..199d16ed
--- /dev/null
+++ b/Speech2Speech/gradle.properties
@@ -0,0 +1,20 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
diff --git a/Speech2Speech/gradle/wrapper/gradle-wrapper.jar b/Speech2Speech/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..f6b961fd
Binary files /dev/null and b/Speech2Speech/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Speech2Speech/gradle/wrapper/gradle-wrapper.properties b/Speech2Speech/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..85fae825
--- /dev/null
+++ b/Speech2Speech/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Aug 20 22:09:11 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/Speech2Speech/gradlew b/Speech2Speech/gradlew
new file mode 100755
index 00000000..cccdd3d5
--- /dev/null
+++ b/Speech2Speech/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/Speech2Speech/gradlew.bat b/Speech2Speech/gradlew.bat
new file mode 100644
index 00000000..e95643d6
--- /dev/null
+++ b/Speech2Speech/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Speech2Speech/settings.gradle b/Speech2Speech/settings.gradle
new file mode 100644
index 00000000..e7b4def4
--- /dev/null
+++ b/Speech2Speech/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/dialogflow/.gitignore b/dialogflow/.gitignore
new file mode 100644
index 00000000..39fb081a
--- /dev/null
+++ b/dialogflow/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/dialogflow/README.md b/dialogflow/README.md
new file mode 100644
index 00000000..f9922efb
--- /dev/null
+++ b/dialogflow/README.md
@@ -0,0 +1,72 @@
+# Dialogflow Sample
+
+This app demonstrates how to make gRPC connections to the [Dialogflow API](https://cloud.google.com/dialogflow-enterprise/)
+
+The app demonstrates how to detect intents:
+- Via Text
+- Via Streaming Audio
+- With Sentiment Analysis
+- With Text-to-Speech
+- With Knowledge Connectors
+
+To call the Dialogflow API from Android, you need to get authorization tokens from Firebase Cloud Messaging for them to be accepted by the Dialogflow API. To get this token, this sample uses a Firebase Function (in Node.js) to generate these tokens on the behalf of a service account to be used by the app when making a request to the Dialogflow API.
+
+## Prerequisites
+- An Android device or emulator
+- Android Studio 3 or later
+
+## Setup
+- Create a project (or use an existing one) in the [Google Cloud Console][cloud-console]
+- Enable the [Dialogflow API](https://console.cloud.google.com/apis/library/dialogflow.googleapis.com).
+- Enable the [IAM Service Account Credentials API](https://pantheon.corp.google.com/apis/library/iamcredentials.googleapis.com).
+- [Enable billing][billing].
+- Be sure that you have gone through the steps by expanding the [Create an agent](https://cloud.google.com/dialogflow-enterprise/docs/quickstart-console#create-an-agent) to create and configure your stopwatch agent.
+- [Import the Dialogflow Agent](https://dialogflow.com/docs/agents/export-import-restore#import) using the `StopwatchAgent.zip` which is located in the `stopwatch` directory.
+- [Create a Service account](https://cloud.google.com/iam/docs/creating-managing-service-accounts) with the following IAM role: `Dialogflow API Client`. Example name: `dialogflow-client`. ([For more info on: how to add roles to a Service Account](https://cloud.google.com/iam/docs/granting-roles-to-service-accounts#granting_access_to_a_service_account_for_a_resource))
+- Under Dialogflow-client, Click on edit permission icon on the right and add another role as follows and save the changes:
+ - Dialogflow Client (Used by the app to make detect intent requests)
+ - Dialogflow Reader (Used by the app to list knowledge bases)
+- Enable beta features for:
+ - [Sentiment Analysis](https://cloud.google.com/dialogflow-enterprise/docs/sentiment#enable_beta_features)
+ - [Text-to-Speech](https://cloud.google.com/dialogflow-enterprise/docs/detect-intent-tts#enable_beta_features)
+ - [Knowledge Connectors](https://cloud.google.com/dialogflow-enterprise/docs/knowledge-connectors#enable_beta_features)
+
+### Setup the app
+-Clone this repository `git clone https://github.com/GoogleCloudPlatform/android-docs-samples.git`
+- Replace GCP_PROJECT_ID in strings.xml with your Project ID
+
+### Setup Firebase on the application:
+- Complete the steps for [Add Firebase to your app](https://firebase.google.com/docs/android/setup) and expand the "Create a Firebase project" section for instructions on how to add project to your Firebase console. Note: No need to complete any other sections, they are already done.
+- In the [Firebase console](https://console.firebase.google.com/), open the "Authentication" section under Develop.
+- On the **Sign-in Methods** page, enable the **Anonymous** sign-in method.
+- Give the package name of the app as `com.google.cloud.examples.dialogflow`
+
+### Setup and Deploy the Firebase Function
+The Firebase Function provides auth tokens to your app, You'll be using a provided sample function to be run with this app.
+
+- Follow the steps in this [guide](https://firebase.google.com/docs/functions/get-started) for:
+ - "1. Set up Node.js and the Firebase CLI"
+ - "2. Initialize Firebase SDK for Cloud Functions".
+- Replace `index.js` file with the [provided index.js](https://github.com/GoogleCloudPlatform/nodejs-docs-samples/blob/master/functions/tokenservice/functions/index.js).
+- Open `index.js`, go to function "generateAccessToken", and replace “SERVICE-ACCOUNT-NAME@YOUR_PROJECT_ID.iam.gserviceaccount.com” with your Service account name (`dialogflow-client`) and project id.
+- Deploy getOAuthToken method by running command:
+```
+firebase deploy --only functions
+```
+- For your "App Engine Default Service Account" add the following IAM role: `Service Account Token Creator` . ([For more info on: how to add roles to a Service Account](https://cloud.google.com/iam/docs/granting-roles-to-service-accounts#granting_access_to_a_service_account_for_a_resource))
+
+- For more info please refer (https://firebase.google.com/docs/functions/get-started).
+
+## Run the app
+- You are now ready to build and run the project. In Android Studio you can do this by clicking the 'Play' button in the toolbar. This will launch the app on the emulator or on the device you've selected.
+- As soon the app launches, it will ask for the google sign-in.
+- After successful signing in, choose the option by selecting a checkbox and click on chat button
+- Type the message to send and click on the send button on the bottom right.
+- Alternatively tap on the mic button to speak and send the message to the Dialogflow.
+
+
+[cloud-console]: https://console.cloud.google.com
+[git]: https://git-scm.com/
+[android-studio]: https://developer.android.com/studio
+[billing]: https://console.cloud.google.com/billing?project=_
+[Firebase]: https://firebase.google.com/
diff --git a/dialogflow/StopwatchAgent.zip b/dialogflow/StopwatchAgent.zip
new file mode 100644
index 00000000..50ac1093
Binary files /dev/null and b/dialogflow/StopwatchAgent.zip differ
diff --git a/dialogflow/app/.gitignore b/dialogflow/app/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/dialogflow/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/dialogflow/app/build.gradle b/dialogflow/app/build.gradle
new file mode 100644
index 00000000..38f3eda9
--- /dev/null
+++ b/dialogflow/app/build.gradle
@@ -0,0 +1,63 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.google.cloud.examples.dialogflow"
+ minSdkVersion 26
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ multiDexEnabled true
+ }
+ buildTypes {
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ packagingOptions {
+ exclude 'META-INF/DEPENDENCIES'
+ exclude 'META-INF/LICENSE'
+ exclude 'META-INF/LICENSE.txt'
+ exclude 'META-INF/license.txt'
+ exclude 'META-INF/NOTICE'
+ exclude 'META-INF/NOTICE.txt'
+ exclude 'META-INF/notice.txt'
+ exclude 'META-INF/ASL2.0'
+ exclude 'META-INF/INDEX.LIST'
+ exclude 'META-INF/io.netty.versions.properties'
+ }
+}
+
+dependencies {
+
+ implementation 'com.android.support:appcompat-v7:28.0.0'
+ implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+ implementation 'com.android.support:design:28.0.0'
+ configurations.all {
+ resolutionStrategy.force 'com.android.support:support-annotations:25.3.0'
+ }
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'com.google.android.material:material:1.0.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.2.0'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+ implementation 'com.google.firebase:firebase-core:17.1.0'
+ implementation 'com.google.firebase:firebase-auth:19.0.0'
+ implementation 'com.firebaseui:firebase-ui-auth:4.1.0'
+ implementation 'com.google.firebase:firebase-messaging:20.0.0'
+ implementation 'com.google.firebase:firebase-functions:19.0.0'
+ implementation 'com.android.volley:volley:1.1.1'
+ implementation 'com.google.cloud:google-cloud-dialogflow:0.98.0-alpha'
+ implementation group: 'io.grpc', name: 'grpc-okhttp', version: '1.21.0'
+ implementation group: 'io.grpc', name: 'grpc-netty', version: '1.21.0'
+
+}
+
+
+apply plugin: 'com.google.gms.google-services'
diff --git a/dialogflow/app/gradle/wrapper/gradle-wrapper.jar b/dialogflow/app/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..f6b961fd
Binary files /dev/null and b/dialogflow/app/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/dialogflow/app/gradle/wrapper/gradle-wrapper.properties b/dialogflow/app/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..e03e4ccf
--- /dev/null
+++ b/dialogflow/app/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Jul 22 11:29:16 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/dialogflow/app/gradlew b/dialogflow/app/gradlew
new file mode 100644
index 00000000..cccdd3d5
--- /dev/null
+++ b/dialogflow/app/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/dialogflow/app/gradlew.bat b/dialogflow/app/gradlew.bat
new file mode 100644
index 00000000..e95643d6
--- /dev/null
+++ b/dialogflow/app/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/dialogflow/app/proguard-rules.pro b/dialogflow/app/proguard-rules.pro
new file mode 100644
index 00000000..f1b42451
--- /dev/null
+++ b/dialogflow/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/dialogflow/app/src/androidTest/java/com/google/cloud/examples/dialogflow/ExampleInstrumentedTest.java b/dialogflow/app/src/androidTest/java/com/google/cloud/examples/dialogflow/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..98bb2547
--- /dev/null
+++ b/dialogflow/app/src/androidTest/java/com/google/cloud/examples/dialogflow/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.google.cloud.examples.dialogflow;
+
+import android.content.Context;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.example.mlapitest", appContext.getPackageName());
+ }
+}
diff --git a/dialogflow/app/src/main/AndroidManifest.xml b/dialogflow/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..02eef2da
--- /dev/null
+++ b/dialogflow/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/AppController.java b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/AppController.java
new file mode 100644
index 00000000..5fd06c4a
--- /dev/null
+++ b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/AppController.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.examples.dialogflow;
+
+import android.app.Application;
+import android.media.MediaPlayer;
+import android.os.Environment;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class AppController extends Application {
+
+ public static final String TOKEN_RECEIVED = "TOKEN_RECEIVED";
+ public static final String SESSION_ID = "sessionId";
+ public static String PROJECT_ID = "";
+
+ public static void playAudio(byte[] byteArray) {
+ MediaPlayer mediaPlayer = new MediaPlayer();
+ try {
+ File tempFile = File.createTempFile("dialogFlow", null,
+ Environment.getExternalStorageDirectory());
+ tempFile.deleteOnExit();
+ FileOutputStream fos = new FileOutputStream(tempFile);
+ fos.write(byteArray);
+ fos.close();
+ mediaPlayer.reset();
+ FileInputStream fis = new FileInputStream(tempFile);
+ mediaPlayer.setDataSource(fis.getFD());
+
+ mediaPlayer.prepare();
+ mediaPlayer.start();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ PROJECT_ID = getApplicationContext().getString(R.string.gcp_project_id);
+ }
+
+}
diff --git a/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/adapter/ChatRecyclerViewAdapter.java b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/adapter/ChatRecyclerViewAdapter.java
new file mode 100644
index 00000000..7e793002
--- /dev/null
+++ b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/adapter/ChatRecyclerViewAdapter.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.examples.dialogflow.adapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.cloud.examples.dialogflow.R;
+import com.google.cloud.examples.dialogflow.model.ChatMsgModel;
+
+import java.util.ArrayList;
+
+public class ChatRecyclerViewAdapter extends RecyclerView.Adapter {
+
+ private ArrayList chatMsgModels;
+
+ public ChatRecyclerViewAdapter(ArrayList chatMsgModels) {
+ this.chatMsgModels = chatMsgModels;
+
+ }
+
+ @Override
+ public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View itemView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.custom_chat_recyclerview_item, parent, false);
+
+ return new MyViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(MyViewHolder holder, final int position) {
+ final ChatMsgModel chatMsgModel = chatMsgModels.get(position);
+
+ if (chatMsgModel.getType() == 1) { // Message Sent
+ holder.tvMsgSent.setText(chatMsgModel.getMsg());
+ holder.tvMsgSent.setVisibility(View.VISIBLE);
+ holder.tvMsgReceived.setVisibility(View.GONE);
+ } else {
+ holder.tvMsgReceived.setText(chatMsgModel.getMsg());
+ holder.tvMsgReceived.setVisibility(View.VISIBLE);
+ holder.tvMsgSent.setVisibility(View.GONE);
+ }
+
+
+ }
+
+ @Override
+ public int getItemCount() {
+ return chatMsgModels.size();
+ }
+
+ public class MyViewHolder extends RecyclerView.ViewHolder {
+ RelativeLayout rlMain;
+ TextView tvMsgSent;
+ TextView tvMsgReceived;
+
+ public MyViewHolder(View view) {
+ super(view);
+ rlMain = view.findViewById(R.id.rlMain);
+ tvMsgSent = view.findViewById(R.id.tvMsgSent);
+ tvMsgReceived = view.findViewById(R.id.tvMsgReceived);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/model/ChatMsgModel.java b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/model/ChatMsgModel.java
new file mode 100644
index 00000000..8aca92f9
--- /dev/null
+++ b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/model/ChatMsgModel.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.examples.dialogflow.model;
+
+public class ChatMsgModel {
+
+ private String msg;
+ private int type;
+
+ public ChatMsgModel(String msg, int type) {
+ this.msg = msg;
+ this.type = type;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+}
diff --git a/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/service/MyFirebaseCloudMessagingService.java b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/service/MyFirebaseCloudMessagingService.java
new file mode 100644
index 00000000..71cff4a3
--- /dev/null
+++ b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/service/MyFirebaseCloudMessagingService.java
@@ -0,0 +1,59 @@
+
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.examples.dialogflow.service;
+
+import android.content.Intent;
+import android.util.Log;
+
+import com.google.cloud.examples.dialogflow.AppController;
+import com.google.cloud.examples.dialogflow.utils.AuthUtils;
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+
+public class MyFirebaseCloudMessagingService extends FirebaseMessagingService {
+
+ private static final String TAG = MyFirebaseCloudMessagingService.class.getSimpleName();
+
+ @Override
+ public void onMessageReceived(RemoteMessage remoteMessage) {
+ Log.i("FirebaseMessage", "From: " + remoteMessage.getFrom());
+
+ // Check if message contains a notification payload.
+ if (remoteMessage.getNotification() != null) {
+ Log.i(TAG, "Notification Body: " + remoteMessage.getNotification().getBody());
+ handleNotification(remoteMessage.getNotification().getTitle(),
+ remoteMessage.getNotification().getBody());
+ }
+ }
+
+ /**
+ * function to save the token data in the AppController
+ *
+ * @param expiryTime : expiry time received from FCM
+ * @param token : token received from FCM
+ */
+ private void handleNotification(String expiryTime, String token) {
+
+ AuthUtils.setExpiryTime(expiryTime);
+ AuthUtils.token = token;
+
+ Intent intent = new Intent(AppController.TOKEN_RECEIVED);
+ sendBroadcast(intent);
+ }
+
+}
\ No newline at end of file
diff --git a/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/ui/ChatActivity.java b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/ui/ChatActivity.java
new file mode 100644
index 00000000..eaa2e140
--- /dev/null
+++ b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/ui/ChatActivity.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.examples.dialogflow.ui;
+
+import android.Manifest;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.speech.RecognizerIntent;
+import android.text.TextUtils;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.PopupMenu;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.cloud.examples.dialogflow.AppController;
+import com.google.cloud.examples.dialogflow.R;
+import com.google.cloud.examples.dialogflow.adapter.ChatRecyclerViewAdapter;
+import com.google.cloud.examples.dialogflow.model.ChatMsgModel;
+import com.google.cloud.examples.dialogflow.utils.ApiRequest;
+import com.google.cloud.examples.dialogflow.utils.AuthUtils;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+public class ChatActivity extends AppCompatActivity {
+
+ private static ChatRecyclerViewAdapter chatRecyclerViewAdapter;
+ private static ArrayList chatMsgModels;
+ private static RecyclerView rvChats;
+ private ApiRequest apiRequest;
+
+ private EditText etMsg;
+ private ImageButton btnSend;
+ private ImageButton btnMic;
+ private AlertDialog alert;
+
+ private ImageButton ibMore;
+
+ private boolean tts = false;
+ private boolean knowledge = false;
+ private boolean sentiment = false;
+
+ private String voiceInput = "";
+
+ /**
+ * Broadcast receiver to hide the progress dialog when token is received
+ */
+ private BroadcastReceiver br = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ alert.dismiss();
+ if (!voiceInput.equals("")) {
+ sendMsg(voiceInput);
+ } else if (!etMsg.getText().toString().trim().equals("")) {
+ sendMsg(etMsg.getText().toString().trim());
+ }
+ }
+ };
+
+ /**
+ * function to scroll the recyclerview at the bottom after each message sent or received
+ */
+ private static void scrollToBottom() {
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ if (chatMsgModels.size() > 0) {
+ rvChats.smoothScrollToPosition(chatMsgModels.size() - 1);
+ }
+ }
+ });
+ }
+
+ /**
+ * function to addMessage in the recyclerview
+ *
+ * @param msg : message to add
+ * @param type : Type of message (sent|received)
+ */
+ private void addMsg(String msg, int type) {
+ chatMsgModels.add(new ChatMsgModel(msg, type));
+ chatRecyclerViewAdapter.notifyDataSetChanged();
+ scrollToBottom();
+ }
+
+ /**
+ * function to show the progress dialog
+ */
+ private void showProgressDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage("Fetching auth token...");
+ builder.setCancelable(false);
+
+ alert = builder.create();
+ alert.show();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_chat);
+
+ if (AppController.PROJECT_ID.equals("GCP_PROJECT_ID")) {
+ Toast.makeText(this, "Please update the GCP_PROJECT_ID in strings.xml",
+ Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ checkPermissions();
+
+ AuthUtils.signInAnonymously(this);
+ AuthUtils.getFirebaseInstanceId();
+
+ initViews();
+ setupRecyclerView();
+ initListeners();
+
+ apiRequest = new ApiRequest();
+
+ }
+
+ /**
+ * function to initialize the views
+ */
+ private void initViews() {
+ etMsg = findViewById(R.id.etMsg);
+ btnSend = findViewById(R.id.btnSend);
+ btnMic = findViewById(R.id.btnMic);
+ rvChats = findViewById(R.id.rvChat);
+ ibMore = findViewById(R.id.ibMore);
+ }
+
+ /**
+ * function to initialize the recyclerview
+ */
+ private void setupRecyclerView() {
+ rvChats.setLayoutManager(new LinearLayoutManager(this));
+ chatMsgModels = new ArrayList<>();
+
+ chatRecyclerViewAdapter = new ChatRecyclerViewAdapter(chatMsgModels);
+ rvChats.setAdapter(chatRecyclerViewAdapter);
+ }
+
+ /**
+ * function to initialize the onClick listeners
+ */
+ private void initListeners() {
+ btnSend.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (AuthUtils.checkSignIn()) {
+ sendMsg(etMsg.getText().toString());
+ scrollToBottom();
+ } else {
+ AuthUtils.signInAnonymously(ChatActivity.this);
+ }
+ }
+ });
+
+ btnMic.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (AuthUtils.checkSignIn()) {
+ promptSpeechInput();
+ }
+ }
+ });
+
+ ibMore.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showMorePopup();
+ }
+ });
+ }
+
+
+ /**
+ * function to send the message
+ *
+ * @param msg : message sent from user
+ */
+ private void sendMsg(String msg) {
+ if (!TextUtils.isEmpty(msg)) {
+ // check if the token is received and expiry time is received and not expired
+ if (AuthUtils.isTokenValid()) {
+ addMsg(msg, 1);
+ etMsg.setText("");
+ voiceInput = "";
+ new APIRequest(AuthUtils.token, AuthUtils.expiryTime, msg, tts, sentiment,
+ knowledge).execute();
+ } else {
+ // get new token if expired or not received
+ getNewToken();
+ }
+ } else {
+ Toast.makeText(this, "Please enter or say some message to send.",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void getNewToken() {
+ showProgressDialog();
+ AuthUtils.callFirebaseFunction();
+ }
+
+ private void promptSpeechInput() {
+ Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
+ RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
+ intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1000);
+ intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "en-US");
+ intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
+ "Speak");
+ try {
+ startActivityForResult(intent, 101);
+ } catch (ActivityNotFoundException a) {
+ Toast.makeText(getApplicationContext(),
+ "Not Supported",
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resultCode == RESULT_OK) {
+ if (requestCode == 101) {
+ ArrayList result = data
+ .getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
+ voiceInput = result.get(0);
+ sendMsg(result.get(0));
+ }
+ }
+ }
+
+ public void checkPermissions() {
+
+ ArrayList arrPerm = new ArrayList<>();
+ arrPerm.add(Manifest.permission.INTERNET);
+ arrPerm.add(Manifest.permission.RECORD_AUDIO);
+ arrPerm.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ arrPerm.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+
+
+ if (!arrPerm.isEmpty()) {
+ String[] permissions = new String[arrPerm.size()];
+ permissions = arrPerm.toArray(permissions);
+ ActivityCompat.requestPermissions(this, permissions, 1);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ IntentFilter intentFilter = new IntentFilter(AppController.TOKEN_RECEIVED);
+ registerReceiver(br, intentFilter);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (br != null) {
+ try {
+ unregisterReceiver(br);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ public void showMorePopup() {
+ PopupMenu popup = new PopupMenu(this, ibMore);
+ popup.inflate(R.menu.main_menu);
+ popup.getMenu().findItem(R.id.action_tts).setChecked(tts);
+ popup.getMenu().findItem(R.id.action_sentiment).setChecked(sentiment);
+ popup.getMenu().findItem(R.id.action_knowledge).setChecked(knowledge);
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+
+ switch (item.getItemId()) {
+ case R.id.action_tts:
+ tts = !tts;
+ item.setChecked(!item.isChecked());
+ break;
+ case R.id.action_sentiment:
+ sentiment = !sentiment;
+ item.setChecked(!item.isChecked());
+ break;
+ case R.id.action_knowledge:
+ knowledge = !knowledge;
+ item.setChecked(!item.isChecked());
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+ });
+ popup.show();
+ }
+
+ private class APIRequest extends AsyncTask {
+ private String token;
+ private Date expiryTime;
+ private String msg;
+ private boolean tts;
+ private boolean sentiment;
+ private boolean knowledge;
+
+ public APIRequest(String token, Date expiryTime, String msg, boolean tts, boolean sentiment,
+ boolean knowledge) {
+ this.token = token;
+ this.expiryTime = expiryTime;
+ this.msg = msg;
+ this.tts = tts;
+ this.sentiment = sentiment;
+ this.knowledge = knowledge;
+ }
+
+ @Override
+ protected String doInBackground(Void... voids) {
+ return apiRequest.callAPI(token, expiryTime, msg, tts, sentiment, knowledge);
+ }
+
+ @Override
+ protected void onPostExecute(String response) {
+ super.onPostExecute(response);
+ addMsg(response, 0);
+ }
+ }
+
+}
diff --git a/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/utils/ApiRequest.java b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/utils/ApiRequest.java
new file mode 100644
index 00000000..e19958ad
--- /dev/null
+++ b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/utils/ApiRequest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.examples.dialogflow.utils;
+
+import com.google.api.gax.core.FixedCredentialsProvider;
+import com.google.auth.Credentials;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
+import com.google.cloud.dialogflow.v2beta1.DetectIntentRequest;
+import com.google.cloud.dialogflow.v2beta1.DetectIntentResponse;
+import com.google.cloud.dialogflow.v2beta1.KnowledgeAnswers;
+import com.google.cloud.dialogflow.v2beta1.KnowledgeBasesSettings;
+import com.google.cloud.dialogflow.v2beta1.OutputAudioConfig;
+import com.google.cloud.dialogflow.v2beta1.OutputAudioEncoding;
+import com.google.cloud.dialogflow.v2beta1.QueryInput;
+import com.google.cloud.dialogflow.v2beta1.QueryParameters;
+import com.google.cloud.dialogflow.v2beta1.QueryResult;
+import com.google.cloud.dialogflow.v2beta1.SentimentAnalysisRequestConfig;
+import com.google.cloud.dialogflow.v2beta1.SessionName;
+import com.google.cloud.dialogflow.v2beta1.SessionsClient;
+import com.google.cloud.dialogflow.v2beta1.SessionsSettings;
+import com.google.cloud.dialogflow.v2beta1.TextInput;
+import com.google.cloud.examples.dialogflow.AppController;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+public class ApiRequest {
+
+ private String token = null;
+ private Date tokenExpiration = null;
+
+ public ApiRequest() {
+ // Variables needed to retrieve an auth token
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'",
+ java.util.Locale.getDefault());
+ simpleDateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ /**
+ * function to call: detect Intent Sentiment Analysis | Detect Intent With TTS | KnowledgeBase
+ *
+ * @param accessToken : access token received from fcm
+ * @param expiryTime : expiry time received from fcm
+ * @param msg : message sent by the user
+ * @param tts : send message to text to speech if true
+ * @param sentiment : send message to sentiment analysis if true
+ * @param knowledge : send message to knowledge base if true
+ * @return : response from the server
+ */
+ public String callAPI(String accessToken, Date expiryTime, String msg, boolean tts,
+ boolean sentiment, boolean knowledge) {
+ this.token = accessToken;
+ this.tokenExpiration = expiryTime;
+ return detectIntent(msg, tts, sentiment, knowledge);
+ }
+
+ /**
+ * function to getting the results from the dialogflow
+ *
+ * @param msg : message sent by the user
+ * @param tts : send message to text to speech if true
+ * @param sentiment : send message to sentiment analysis if true
+ * @param knowledge : send message to knowledge base if true
+ * @return : response from the server
+ */
+ private String detectIntent(String msg, boolean tts, boolean sentiment, boolean knowledge) {
+ try {
+ AccessToken accessToken = new AccessToken(token, tokenExpiration);
+ Credentials credentials = GoogleCredentials.create(accessToken);
+ FixedCredentialsProvider fixedCredentialsProvider =
+ FixedCredentialsProvider.create(credentials);
+ SessionsSettings sessionsSettings = SessionsSettings.newBuilder()
+ .setCredentialsProvider(fixedCredentialsProvider).build();
+ SessionsClient sessionsClient = SessionsClient.create(sessionsSettings);
+ SessionName sessionName = SessionName.of(AppController.PROJECT_ID,
+ AppController.SESSION_ID);
+
+ // Set the text (hello) and language code (en-US) for the query
+ TextInput textInput = TextInput.newBuilder()
+ .setText(msg)
+ .setLanguageCode("en-US")
+ .build();
+
+ // Build the query with the TextInput
+ QueryInput queryInput = QueryInput.newBuilder()
+ .setText(textInput)
+ .build();
+
+ DetectIntentRequest detectIntentRequest = getDetectIntentRequest(sessionName,
+ queryInput, tts, sentiment, knowledge, fixedCredentialsProvider);
+ DetectIntentResponse detectIntentResponse =
+ sessionsClient.detectIntent(detectIntentRequest);
+
+ sessionsClient.close();
+
+ if (tts) {
+ AppController.playAudio(detectIntentResponse.getOutputAudio().toByteArray());
+ }
+
+ return handleResults(detectIntentResponse);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ return ex.getMessage();
+ }
+ }
+
+ /**
+ * function to handle the results
+ *
+ * @param detectIntentResponse : detectIntentResponse object
+ * @return : String response
+ */
+ private String handleResults(DetectIntentResponse detectIntentResponse) {
+ QueryResult queryResult = detectIntentResponse.getQueryResult();
+ StringBuilder response = new StringBuilder();
+
+ KnowledgeAnswers knowledgeAnswers = queryResult.getKnowledgeAnswers();
+ for (KnowledgeAnswers.Answer answer : knowledgeAnswers.getAnswersList()) {
+ response.append(answer.getAnswer()).append("\n");
+ }
+
+ response.append(queryResult.getFulfillmentText());
+
+ if (queryResult.hasSentimentAnalysisResult()) {
+ String magnitude = String.format("(Magnitude: %s; ",
+ queryResult.getSentimentAnalysisResult().getQueryTextSentiment()
+ .getMagnitude());
+ response.append(magnitude);
+
+ String score = String.format("score: %s)",
+ queryResult.getSentimentAnalysisResult().getQueryTextSentiment()
+ .getScore());
+ response.append(score);
+ }
+
+ return response.toString();
+ }
+
+ /**
+ * function to get the DetectIntentRequest object
+ *
+ * @param sessionName : sessionName object
+ * @param queryInput : queryInput object
+ * @param tts : if text to speech is true
+ * @param sentiment : if sentiment analysis is true
+ * @param knowledge : if knowledge base is true
+ * @param fixedCredentialsProvider : fixedCredentialsProvider for knowledgebase
+ * @return : DetectIntentRequest object
+ */
+ private DetectIntentRequest getDetectIntentRequest(SessionName sessionName,
+ QueryInput queryInput, boolean tts,
+ boolean sentiment, boolean knowledge,
+ FixedCredentialsProvider fixedCredentialsProvider)
+ throws Exception {
+ DetectIntentRequest.Builder detectIntentRequestBuilder = DetectIntentRequest.newBuilder()
+ .setSession(sessionName.toString())
+ .setQueryInput(queryInput);
+
+ QueryParameters.Builder queryParametersBuilder = QueryParameters.newBuilder();
+
+ if (tts) {
+ OutputAudioEncoding audioEncoding = OutputAudioEncoding.OUTPUT_AUDIO_ENCODING_MP3;
+ int sampleRateHertz = 16000;
+ OutputAudioConfig outputAudioConfig = OutputAudioConfig.newBuilder()
+ .setAudioEncoding(audioEncoding)
+ .setSampleRateHertz(sampleRateHertz)
+ .build();
+
+ detectIntentRequestBuilder.setOutputAudioConfig(outputAudioConfig);
+ }
+
+ if (sentiment) {
+ SentimentAnalysisRequestConfig sentimentAnalysisRequestConfig =
+ SentimentAnalysisRequestConfig.newBuilder()
+ .setAnalyzeQueryTextSentiment(true).build();
+
+ queryParametersBuilder
+ .setSentimentAnalysisRequestConfig(sentimentAnalysisRequestConfig);
+ }
+
+
+ if (knowledge) {
+ KnowledgeBasesSettings knowledgeSessionsSettings = KnowledgeBasesSettings.newBuilder()
+ .setCredentialsProvider(fixedCredentialsProvider).build();
+ ArrayList knowledgeBaseNames =
+ KnowledgeBaseUtils.listKnowledgeBases(
+ AppController.PROJECT_ID, knowledgeSessionsSettings);
+
+ if (knowledgeBaseNames.size() > 0) {
+ // As an example, we'll only grab the first Knowledge Base
+ queryParametersBuilder.addKnowledgeBaseNames(knowledgeBaseNames.get(0));
+ }
+ }
+
+ QueryParameters queryParameters = queryParametersBuilder.build();
+ detectIntentRequestBuilder.setQueryParams(queryParameters);
+
+ return detectIntentRequestBuilder.build();
+ }
+}
+
+
diff --git a/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/utils/AuthUtils.java b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/utils/AuthUtils.java
new file mode 100644
index 00000000..28366fab
--- /dev/null
+++ b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/utils/AuthUtils.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.examples.dialogflow.utils;
+
+import android.app.Activity;
+import android.util.Log;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+
+import com.google.android.gms.tasks.OnCompleteListener;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.android.gms.tasks.Task;
+import com.google.firebase.auth.AuthResult;
+import com.google.firebase.auth.FirebaseAuth;
+import com.google.firebase.functions.FirebaseFunctions;
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.iid.InstanceIdResult;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+public class AuthUtils {
+
+ private static String firebaseInstanceId = "";
+ private static FirebaseAuth firebaseAuth;
+
+ public static String token = "";
+ public static Date expiryTime;
+
+ /**
+ * function to call the firebase function which will send the fcm message containing token and
+ * expiry time to the device
+ */
+ public static void callFirebaseFunction() {
+ Map data = new HashMap<>();
+ data.put("deviceID", firebaseInstanceId);
+
+ FirebaseFunctions.getInstance()
+ .getHttpsCallable("getOAuthToken")
+ .call(data);
+ }
+
+ /**
+ * function to store the token expiry time
+ * @param expiryTime : expiry time in UTC timezone
+ */
+ public static void setExpiryTime(String expiryTime) {
+ AuthUtils.expiryTime = getConvertedDateTime(expiryTime);
+ }
+
+ /**
+ * function to convert the time from UTC to local TimeZone
+ * @param expiryTime : expiry time in UTC timezone
+ * @return Date : converted datetime to local timezonne
+ */
+ private static Date getConvertedDateTime(String expiryTime) {
+ try {
+ final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+ DateTimeFormatter format = DateTimeFormatter.ofPattern(DATE_FORMAT);
+ LocalDateTime ldt = LocalDateTime.parse(expiryTime, format);
+ ZoneId fromZoneId = ZoneId.of(TimeZone.getTimeZone("UTC").getID());
+ ZonedDateTime fromZoneDateTime = ldt.atZone(fromZoneId);
+ ZoneId currentZoneId = TimeZone.getDefault().toZoneId();
+ ZonedDateTime zonedDateTime = fromZoneDateTime.withZoneSameInstant(currentZoneId);
+ return new SimpleDateFormat(DATE_FORMAT, Locale.US).parse(format.format(zonedDateTime));
+ } catch(Exception ex) {
+ ex.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * function to signin to Firebase Anonymously
+ * @param activity : Instance of the Activity
+ */
+ public static void signInAnonymously(final Activity activity) {
+ firebaseAuth = FirebaseAuth.getInstance();
+ firebaseAuth.signInAnonymously()
+ .addOnCompleteListener(activity, new OnCompleteListener() {
+ @Override
+ public void onComplete(@NonNull Task task) {
+ if (task.isSuccessful()) {
+ // Sign in success, update UI with the signed-in user's information
+ Toast.makeText(activity, "Sign In was successful",
+ Toast.LENGTH_SHORT).show();
+ } else {
+ // If sign in fails, display a message to the user.
+ Toast.makeText(activity, "Authentication failed.",
+ Toast.LENGTH_SHORT).show();
+ }
+
+ }
+ });
+ }
+
+ /**
+ * function to check the user is logged in
+ *
+ * @return boolean : returns true if user is logged inn
+ */
+ public static boolean checkSignIn() {
+ return firebaseAuth != null && firebaseAuth.getCurrentUser() != null;
+ }
+
+ /**
+ * function to get the firebase instance id
+ */
+ public static void getFirebaseInstanceId() {
+ FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(
+ new OnSuccessListener() {
+ @Override
+ public void onSuccess(InstanceIdResult instanceIdResult) {
+ String deviceToken = instanceIdResult.getToken();
+ firebaseInstanceId = deviceToken;
+ Log.i("fcmId", deviceToken);
+ }
+ });
+ }
+
+ /**
+ * function to check if the token is valid
+ * @return boolean : indicates the status of the signin
+ */
+ public static boolean isTokenValid() {
+ return AuthUtils.expiryTime != null && !AuthUtils.token.equals("")
+ && AuthUtils.expiryTime.getTime() > System.currentTimeMillis();
+ }
+
+}
diff --git a/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/utils/KnowledgeBaseUtils.java b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/utils/KnowledgeBaseUtils.java
new file mode 100644
index 00000000..db406dc5
--- /dev/null
+++ b/dialogflow/app/src/main/java/com/google/cloud/examples/dialogflow/utils/KnowledgeBaseUtils.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.cloud.examples.dialogflow.utils;
+
+
+import com.google.cloud.dialogflow.v2beta1.KnowledgeBase;
+import com.google.cloud.dialogflow.v2beta1.KnowledgeBasesClient;
+import com.google.cloud.dialogflow.v2beta1.KnowledgeBasesSettings;
+import com.google.cloud.dialogflow.v2beta1.ProjectName;
+
+import java.util.ArrayList;
+
+public class KnowledgeBaseUtils {
+
+ /**
+ * List Knowledge bases
+ *
+ * @param projectId Project/agent id.
+ */
+ public static ArrayList listKnowledgeBases(String projectId,
+ KnowledgeBasesSettings knowledgeBasesSettings)
+ throws Exception {
+ ArrayList ids = new ArrayList<>();
+ // Instantiates a client
+ try (KnowledgeBasesClient knowledgeBasesClient =
+ KnowledgeBasesClient.create(knowledgeBasesSettings)) {
+ ProjectName projectName = ProjectName.of(projectId);
+ for (KnowledgeBase knowledgeBase :
+ knowledgeBasesClient.listKnowledgeBases(projectName).iterateAll()) {
+ ids.add(knowledgeBase.getName());
+ }
+ }
+
+ return ids;
+ }
+}
diff --git a/dialogflow/app/src/main/res/drawable-hdpi/ic_mic.png b/dialogflow/app/src/main/res/drawable-hdpi/ic_mic.png
new file mode 100755
index 00000000..626b816a
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-hdpi/ic_mic.png differ
diff --git a/dialogflow/app/src/main/res/drawable-hdpi/ic_more_vert.png b/dialogflow/app/src/main/res/drawable-hdpi/ic_more_vert.png
new file mode 100755
index 00000000..51041c2e
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-hdpi/ic_more_vert.png differ
diff --git a/dialogflow/app/src/main/res/drawable-hdpi/ic_send.png b/dialogflow/app/src/main/res/drawable-hdpi/ic_send.png
new file mode 100755
index 00000000..04c04401
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-hdpi/ic_send.png differ
diff --git a/dialogflow/app/src/main/res/drawable-mdpi/ic_mic.png b/dialogflow/app/src/main/res/drawable-mdpi/ic_mic.png
new file mode 100755
index 00000000..db2e0156
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-mdpi/ic_mic.png differ
diff --git a/dialogflow/app/src/main/res/drawable-mdpi/ic_more_vert.png b/dialogflow/app/src/main/res/drawable-mdpi/ic_more_vert.png
new file mode 100755
index 00000000..00e448a7
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-mdpi/ic_more_vert.png differ
diff --git a/dialogflow/app/src/main/res/drawable-mdpi/ic_send.png b/dialogflow/app/src/main/res/drawable-mdpi/ic_send.png
new file mode 100755
index 00000000..6c9e2983
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-mdpi/ic_send.png differ
diff --git a/dialogflow/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/dialogflow/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..c7bd21db
--- /dev/null
+++ b/dialogflow/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dialogflow/app/src/main/res/drawable-xhdpi/ic_mic.png b/dialogflow/app/src/main/res/drawable-xhdpi/ic_mic.png
new file mode 100755
index 00000000..96032684
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-xhdpi/ic_mic.png differ
diff --git a/dialogflow/app/src/main/res/drawable-xhdpi/ic_more_vert.png b/dialogflow/app/src/main/res/drawable-xhdpi/ic_more_vert.png
new file mode 100755
index 00000000..6925c884
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-xhdpi/ic_more_vert.png differ
diff --git a/dialogflow/app/src/main/res/drawable-xhdpi/ic_send.png b/dialogflow/app/src/main/res/drawable-xhdpi/ic_send.png
new file mode 100755
index 00000000..8e78bf0a
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-xhdpi/ic_send.png differ
diff --git a/dialogflow/app/src/main/res/drawable-xxhdpi/ic_mic.png b/dialogflow/app/src/main/res/drawable-xxhdpi/ic_mic.png
new file mode 100755
index 00000000..7184522f
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-xxhdpi/ic_mic.png differ
diff --git a/dialogflow/app/src/main/res/drawable-xxhdpi/ic_more_vert.png b/dialogflow/app/src/main/res/drawable-xxhdpi/ic_more_vert.png
new file mode 100755
index 00000000..08bfd003
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-xxhdpi/ic_more_vert.png differ
diff --git a/dialogflow/app/src/main/res/drawable-xxhdpi/ic_send.png b/dialogflow/app/src/main/res/drawable-xxhdpi/ic_send.png
new file mode 100755
index 00000000..d589b778
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-xxhdpi/ic_send.png differ
diff --git a/dialogflow/app/src/main/res/drawable-xxxhdpi/ic_mic.png b/dialogflow/app/src/main/res/drawable-xxxhdpi/ic_mic.png
new file mode 100755
index 00000000..227df2b0
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-xxxhdpi/ic_mic.png differ
diff --git a/dialogflow/app/src/main/res/drawable-xxxhdpi/ic_more_vert.png b/dialogflow/app/src/main/res/drawable-xxxhdpi/ic_more_vert.png
new file mode 100755
index 00000000..e16d58f4
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-xxxhdpi/ic_more_vert.png differ
diff --git a/dialogflow/app/src/main/res/drawable-xxxhdpi/ic_send.png b/dialogflow/app/src/main/res/drawable-xxxhdpi/ic_send.png
new file mode 100755
index 00000000..08172491
Binary files /dev/null and b/dialogflow/app/src/main/res/drawable-xxxhdpi/ic_send.png differ
diff --git a/dialogflow/app/src/main/res/drawable/ic_launcher_background.xml b/dialogflow/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..d5fccc53
--- /dev/null
+++ b/dialogflow/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dialogflow/app/src/main/res/drawable/rounded_corner.xml b/dialogflow/app/src/main/res/drawable/rounded_corner.xml
new file mode 100644
index 00000000..982884bd
--- /dev/null
+++ b/dialogflow/app/src/main/res/drawable/rounded_corner.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dialogflow/app/src/main/res/drawable/rounded_corner_edittext.xml b/dialogflow/app/src/main/res/drawable/rounded_corner_edittext.xml
new file mode 100644
index 00000000..2619e851
--- /dev/null
+++ b/dialogflow/app/src/main/res/drawable/rounded_corner_edittext.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dialogflow/app/src/main/res/layout/activity_chat.xml b/dialogflow/app/src/main/res/layout/activity_chat.xml
new file mode 100644
index 00000000..fbb224cf
--- /dev/null
+++ b/dialogflow/app/src/main/res/layout/activity_chat.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dialogflow/app/src/main/res/layout/content_main.xml b/dialogflow/app/src/main/res/layout/content_main.xml
new file mode 100644
index 00000000..cbaaa0bf
--- /dev/null
+++ b/dialogflow/app/src/main/res/layout/content_main.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dialogflow/app/src/main/res/layout/custom_chat_recyclerview_item.xml b/dialogflow/app/src/main/res/layout/custom_chat_recyclerview_item.xml
new file mode 100644
index 00000000..70d3a8af
--- /dev/null
+++ b/dialogflow/app/src/main/res/layout/custom_chat_recyclerview_item.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dialogflow/app/src/main/res/menu/main_menu.xml b/dialogflow/app/src/main/res/menu/main_menu.xml
new file mode 100644
index 00000000..83fc54a9
--- /dev/null
+++ b/dialogflow/app/src/main/res/menu/main_menu.xml
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/dialogflow/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/dialogflow/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/dialogflow/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/dialogflow/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/dialogflow/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..eca70cfe
--- /dev/null
+++ b/dialogflow/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/dialogflow/app/src/main/res/mipmap-hdpi/ic_launcher.png b/dialogflow/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..898f3ed5
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/dialogflow/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/dialogflow/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..dffca360
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/dialogflow/app/src/main/res/mipmap-mdpi/ic_launcher.png b/dialogflow/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..64ba76f7
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/dialogflow/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/dialogflow/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..dae5e082
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/dialogflow/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/dialogflow/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..e5ed4659
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/dialogflow/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/dialogflow/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..14ed0af3
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/dialogflow/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/dialogflow/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..b0907cac
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/dialogflow/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/dialogflow/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..d8ae0315
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/dialogflow/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/dialogflow/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..2c18de9e
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/dialogflow/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/dialogflow/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..beed3cdd
Binary files /dev/null and b/dialogflow/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/dialogflow/app/src/main/res/values/colors.xml b/dialogflow/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..06feb4ac
--- /dev/null
+++ b/dialogflow/app/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+ #ff9800
+ #ef6c00
+ #FF4081
+ #FFFFFF
+
diff --git a/dialogflow/app/src/main/res/values/strings.xml b/dialogflow/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..e964cbac
--- /dev/null
+++ b/dialogflow/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+Dialogflow
+GCP_PROJECT-ID
+Knowledge Connectors
+Sentiment Analysis
+Text to speech
+Mic
+
diff --git a/dialogflow/app/src/main/res/values/styles.xml b/dialogflow/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..545b9c6d
--- /dev/null
+++ b/dialogflow/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dialogflow/app/src/test/java/com/google/cloud/examples/dialogflow/ExampleUnitTest.java b/dialogflow/app/src/test/java/com/google/cloud/examples/dialogflow/ExampleUnitTest.java
new file mode 100644
index 00000000..66fc3351
--- /dev/null
+++ b/dialogflow/app/src/test/java/com/google/cloud/examples/dialogflow/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.google.cloud.examples.dialogflow;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/dialogflow/build.gradle b/dialogflow/build.gradle
new file mode 100644
index 00000000..41da7c24
--- /dev/null
+++ b/dialogflow/build.gradle
@@ -0,0 +1,28 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.4.2'
+
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ classpath 'com.google.gms:google-services:4.2.0'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/dialogflow/gradle.properties b/dialogflow/gradle.properties
new file mode 100644
index 00000000..9e6fce10
--- /dev/null
+++ b/dialogflow/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+android.enableJetifier=true
+android.useAndroidX=true
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/dialogflow/gradle/wrapper/gradle-wrapper.jar b/dialogflow/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..13372aef
Binary files /dev/null and b/dialogflow/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/dialogflow/gradle/wrapper/gradle-wrapper.properties b/dialogflow/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..0e19bfe6
--- /dev/null
+++ b/dialogflow/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Jun 17 13:01:57 MDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/dialogflow/gradlew b/dialogflow/gradlew
new file mode 100755
index 00000000..9d82f789
--- /dev/null
+++ b/dialogflow/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/dialogflow/gradlew.bat b/dialogflow/gradlew.bat
new file mode 100644
index 00000000..aec99730
--- /dev/null
+++ b/dialogflow/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/dialogflow/settings.gradle b/dialogflow/settings.gradle
new file mode 100644
index 00000000..e7b4def4
--- /dev/null
+++ b/dialogflow/settings.gradle
@@ -0,0 +1 @@
+include ':app'