diff --git a/speech/Speech-to-Speech/.gitignore b/speech/Speech-to-Speech/.gitignore new file mode 100644 index 00000000..2b75303a --- /dev/null +++ b/speech/Speech-to-Speech/.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/speech/Speech-to-Speech/README.md b/speech/Speech-to-Speech/README.md new file mode 100644 index 00000000..9b01f713 --- /dev/null +++ b/speech/Speech-to-Speech/README.md @@ -0,0 +1,67 @@ +# Cloud Speech to Speech Translation Sample + +This app demonstrates how to create a live translation service using the Cloud Speech-To-Text, +Translation, and Text-To-Speech APIs. It uses these apis to: + +- Make streaming gRPC connections to the Cloud Speech API` to recognize speech in recorded audio. +- Send transcripts to the Translate API to get the translation. +- Send translated text to the Text-to-Speech API so that it can be played back to the user. + +To call the Dialogflow API from Android, you need provide authorization tokens with each request. To +get this token, this sample uses a Firebase Function to genereate these tokens on the behalf of a +service account. The token is returned to the app via Firebase Cloud Messaging. + +## 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](https://console.cloud.google.com) +- [Enable billing](https://console.cloud.google.com/billing?project=_) and the + - [Speech API](https://console.cloud.google.com/apis/library/speech.googleapis.com). + - [Translate API](https://console.cloud.google.com/apis/library/translate.googleapis.com). + - [Text-to-Speech API](https://console.cloud.google.com/apis/library/texttospeech.googleapis.com). + - [IAM Service Account Credentials API](https://console.cloud.google.com/apis/library/iamcredentials.googleapis.com). +- [Create a Service account](https://cloud.google.com/iam/docs/creating-managing-service-accounts) with the following IAM role: `Cloud Translation API User`. Example name: `speech-to-speech-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)) + +### Setup the app +- Clone this repository `git clone https://github.com/GoogleCloudPlatform/android-docs-samples.git` +- `cd speech/Speech-to-Speech` +- 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.speechtospeech` + +### 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: + - "2. Set up Node.js and the Firebase CLI" + - "3. 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 the `generateAccessToken` function, and replace`SERVICE-ACCOUNT-NAME@YOUR_PROJECT_ID.iam.gserviceaccount.com` with your Service account name (`speech-to-speech-client`) and project id. +- Deploy getOAuthToken method by running command: +``` +firebase deploy --only functions +``` +- On the GCP console, add the following IAM role: `Service Account Token Creator` to your +"App Engine Default Service Account" ([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)) +- Open the [Firebase console](https://console.firebase.google.com/) + - Select Databases on the side menu + - Select Firestore + - Click `Start collection` + - Title your collection `ShortLivedAuthTOkens` and click Next + - Enter `OauthToken` for the Document title and Save +- 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. diff --git a/speech/Speech-to-Speech/app/.gitignore b/speech/Speech-to-Speech/app/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/speech/Speech-to-Speech/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/speech/Speech-to-Speech/app/build.gradle b/speech/Speech-to-Speech/app/build.gradle new file mode 100644 index 00000000..a66c7973 --- /dev/null +++ b/speech/Speech-to-Speech/app/build.gradle @@ -0,0 +1,60 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + defaultConfig { + applicationId "com.google.cloud.examples.speechtospeech" + minSdkVersion 26 + targetSdkVersion 28 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + multiDexEnabled true + } + 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/speech/Speech-to-Speech/app/gradle/wrapper/gradle-wrapper.jar b/speech/Speech-to-Speech/app/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f6b961fd Binary files /dev/null and b/speech/Speech-to-Speech/app/gradle/wrapper/gradle-wrapper.jar differ diff --git a/speech/Speech-to-Speech/app/gradle/wrapper/gradle-wrapper.properties b/speech/Speech-to-Speech/app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..b8d30e27 --- /dev/null +++ b/speech/Speech-to-Speech/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/speech/Speech-to-Speech/app/gradlew b/speech/Speech-to-Speech/app/gradlew new file mode 100644 index 00000000..cccdd3d5 --- /dev/null +++ b/speech/Speech-to-Speech/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/speech/Speech-to-Speech/app/gradlew.bat b/speech/Speech-to-Speech/app/gradlew.bat new file mode 100644 index 00000000..e95643d6 --- /dev/null +++ b/speech/Speech-to-Speech/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/speech/Speech-to-Speech/app/proguard-rules.pro b/speech/Speech-to-Speech/app/proguard-rules.pro new file mode 100644 index 00000000..f1b42451 --- /dev/null +++ b/speech/Speech-to-Speech/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/speech/Speech-to-Speech/app/src/androidTest/java/com/example/translate/ExampleInstrumentedTest.java b/speech/Speech-to-Speech/app/src/androidTest/java/com/example/translate/ExampleInstrumentedTest.java new file mode 100644 index 00000000..a207101e --- /dev/null +++ b/speech/Speech-to-Speech/app/src/androidTest/java/com/example/translate/ExampleInstrumentedTest.java @@ -0,0 +1,27 @@ +package com.google.cloud.examples.speechtospeech; + +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.google.cloud.examples.speechtospeech", appContext.getPackageName()); + } +} diff --git a/speech/Speech-to-Speech/app/src/main/AndroidManifest.xml b/speech/Speech-to-Speech/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..70203f32 --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/AppController.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/AppController.java new file mode 100644 index 00000000..f4a238d9 --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/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.google.cloud.examples.speechtospeech; + +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/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/adapter/ChatRecyclerViewAdapter.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/adapter/ChatRecyclerViewAdapter.java new file mode 100644 index 00000000..76999acf --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/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.speechtospeech.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.speechtospeech.R; +import com.google.cloud.examples.speechtospeech.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/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/adapter/LanguageAdapter.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/adapter/LanguageAdapter.java new file mode 100644 index 00000000..a2aca79c --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/adapter/LanguageAdapter.java @@ -0,0 +1,72 @@ +package com.google.cloud.examples.speechtospeech.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/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/adapter/VoiceAdapter.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/adapter/VoiceAdapter.java new file mode 100644 index 00000000..0c629f14 --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/adapter/VoiceAdapter.java @@ -0,0 +1,72 @@ +package com.google.cloud.examples.speechtospeech.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/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/model/ChatMsgModel.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/model/ChatMsgModel.java new file mode 100644 index 00000000..98eb711b --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/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.speechtospeech.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/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/service/MyFirebaseCloudMessagingService.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/service/MyFirebaseCloudMessagingService.java new file mode 100644 index 00000000..e624bef2 --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/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.google.cloud.examples.speechtospeech.service; + +import android.content.Intent; +import android.util.Log; + +import com.google.cloud.examples.speechtospeech.AppController; +import com.google.cloud.examples.speechtospeech.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/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/ui/MainActivity.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/ui/MainActivity.java new file mode 100644 index 00000000..a97014a0 --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/ui/MainActivity.java @@ -0,0 +1,296 @@ +package com.google.cloud.examples.speechtospeech.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.google.cloud.examples.speechtospeech.AppController; +import com.google.cloud.examples.speechtospeech.R; +import com.google.cloud.examples.speechtospeech.adapter.ChatRecyclerViewAdapter; +import com.google.cloud.examples.speechtospeech.model.ChatMsgModel; +import com.google.cloud.examples.speechtospeech.utils.ApiRequest; +import com.google.cloud.examples.speechtospeech.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/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/ui/SettingsActivity.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/ui/SettingsActivity.java new file mode 100644 index 00000000..75be1ff6 --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/ui/SettingsActivity.java @@ -0,0 +1,342 @@ +package com.google.cloud.examples.speechtospeech.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.google.cloud.examples.speechtospeech.AppController; +import com.google.cloud.examples.speechtospeech.R; +import com.google.cloud.examples.speechtospeech.adapter.LanguageAdapter; +import com.google.cloud.examples.speechtospeech.adapter.VoiceAdapter; +import com.google.cloud.examples.speechtospeech.utils.ApiRequest; +import com.google.cloud.examples.speechtospeech.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/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/utils/ApiRequest.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/utils/ApiRequest.java new file mode 100644 index 00000000..cc264045 --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/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.google.cloud.examples.speechtospeech.utils; + +import android.content.Intent; +import android.text.TextUtils; +import android.util.Log; + +import com.google.cloud.examples.speechtospeech.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/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/utils/AuthUtils.java b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/utils/AuthUtils.java new file mode 100644 index 00000000..628fe20f --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/java/com/google/cloud/examples/speechtospeech/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.google.cloud.examples.speechtospeech.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/speech/Speech-to-Speech/app/src/main/res/drawable-hdpi/ic_mic.png b/speech/Speech-to-Speech/app/src/main/res/drawable-hdpi/ic_mic.png new file mode 100755 index 00000000..b27f5171 Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-hdpi/ic_mic.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-hdpi/ic_send.png b/speech/Speech-to-Speech/app/src/main/res/drawable-hdpi/ic_send.png new file mode 100755 index 00000000..324421c6 Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-hdpi/ic_send.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-mdpi/ic_mic.png b/speech/Speech-to-Speech/app/src/main/res/drawable-mdpi/ic_mic.png new file mode 100755 index 00000000..68102401 Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-mdpi/ic_mic.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-mdpi/ic_send.png b/speech/Speech-to-Speech/app/src/main/res/drawable-mdpi/ic_send.png new file mode 100755 index 00000000..55475546 Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-mdpi/ic_send.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/speech/Speech-to-Speech/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 00000000..1f6bb290 --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-xhdpi/ic_mic.png b/speech/Speech-to-Speech/app/src/main/res/drawable-xhdpi/ic_mic.png new file mode 100755 index 00000000..41f74c5b Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-xhdpi/ic_mic.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-xhdpi/ic_send.png b/speech/Speech-to-Speech/app/src/main/res/drawable-xhdpi/ic_send.png new file mode 100755 index 00000000..14acf57e Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-xhdpi/ic_send.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-xxhdpi/ic_mic.png b/speech/Speech-to-Speech/app/src/main/res/drawable-xxhdpi/ic_mic.png new file mode 100755 index 00000000..d5da5bee Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-xxhdpi/ic_mic.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-xxhdpi/ic_send.png b/speech/Speech-to-Speech/app/src/main/res/drawable-xxhdpi/ic_send.png new file mode 100755 index 00000000..6c50f9e8 Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-xxhdpi/ic_send.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-xxxhdpi/ic_mic.png b/speech/Speech-to-Speech/app/src/main/res/drawable-xxxhdpi/ic_mic.png new file mode 100755 index 00000000..51419b34 Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-xxxhdpi/ic_mic.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-xxxhdpi/ic_send.png b/speech/Speech-to-Speech/app/src/main/res/drawable-xxxhdpi/ic_send.png new file mode 100755 index 00000000..a081b468 Binary files /dev/null and b/speech/Speech-to-Speech/app/src/main/res/drawable-xxxhdpi/ic_send.png differ diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable-xxxhdpi/rounded_corner.xml b/speech/Speech-to-Speech/app/src/main/res/drawable-xxxhdpi/rounded_corner.xml new file mode 100644 index 00000000..982884bd --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/res/drawable-xxxhdpi/rounded_corner.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/speech/Speech-to-Speech/app/src/main/res/drawable/ic_launcher_background.xml b/speech/Speech-to-Speech/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 00000000..0d025f9b --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/speech/Speech-to-Speech/app/src/main/res/layout/activity_main.xml b/speech/Speech-to-Speech/app/src/main/res/layout/activity_main.xml new file mode 100644 index 00000000..20e71532 --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/speech/Speech-to-Speech/app/src/main/res/layout/activity_settings.xml b/speech/Speech-to-Speech/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 00000000..db78ac9a --- /dev/null +++ b/speech/Speech-to-Speech/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + +