Skip to content
This repository was archived by the owner on Apr 6, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions dialogflow/Dialogflow/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.iml
.gradle
/local.properties
/.idea
.DS_Store
/build
/captures
.externalNativeBuild

app/src/main/res/raw/*.json
44 changes: 44 additions & 0 deletions dialogflow/Dialogflow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Google Cloud Dialogflow Enterprise examples

This directory contains Android example that uses the
[Google Cloud Dialogflow Enterprise](https://cloud.google.com/dialogflow-enterprise/).

## Prerequisites

### Enable Cloud Dialogflow Enterprise

If you have not already done so, [enable Cloud Dialogflow Enterprise for your project](
https://cloud.google.com/dialogflow-enterprise/).

### Set Up to Authenticate With Your Project's Credentials

This Android app uses JSON credential file locally stored in the resources. ***You should not do
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow - Good advice, we really should demo that.

@theacodes FYI - in case you have an objection to this.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This really bad from a security standpoint. I would suggest finding a different route.

this in your production app.*** Instead, you should set up your own backend server that
authenticates app users. The server should delegate API calls from your client app. This way, you
can enforce usage quota per user. Alternatively, you should get the access token on the server side,
and supply client app with it. The access token will expire in a short while.

In this sample, we just put the Service Account in the client for ease of use. The app still gets
an access token using the service account credential, and use the token to call the API, so you can
see how to do so.

In order to try out this sample, visit the [Cloud Console](https://console.cloud.google.com/), and
navigate to:
`API Manager > Credentials > Create credentials > Service account key > New service account`.
Create a new service account, and download the JSON credentials file. Put the file in the app
resources as `app/src/main/res/raw/credential.json`.

Again, ***you should not do this in your production app.***

See the [Cloud Platform Auth Guide](https://cloud.google.com/docs/authentication#developer_workflow)
for more information.

### Project name and agent name for Dialogflow

Open the file `gradle.properties` and change the Dialogflow project name and agent name there.

```
dialogflowProjectName=(your project name here)
dialogflowAgentName(your agent name here)
dialogflowLanguageCode=(language code, such as en-US)
```
1 change: 1 addition & 0 deletions dialogflow/Dialogflow/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice if there was only one of these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how the project is created by Android Studio.

118 changes: 118 additions & 0 deletions dialogflow/Dialogflow/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2017 Google Inc. All Rights Reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think we do All Rights Reserved anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do we do instead? I copied this from other Cloud samples a few years ago.

*
* 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.
*/

apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'

ext {
supportLibraryVersion = '27.1.1'
grpcVersion = '1.14.0'
}

android {
compileSdkVersion 27
defaultConfig {
applicationId 'com.google.cloud.android.dialogflow'
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName '1.0'
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
vectorDrawables.useSupportLibrary = true

// Dialogflow project & agent settings
buildConfigField 'String', 'PROJECT_NAME', "\"${project.property("dialogflowProjectName")}\""
buildConfigField 'String', 'LANGUAGE_CODE', "\"${project.property("dialogflowLanguageCode")}\""
}
buildTypes {
debug {
minifyEnabled false
multiDexEnabled true
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.2'
resolutionStrategy.force "com.android.support:support-annotations:$supportLibraryVersion"
}
lintOptions {
lintConfig file('lint.xml')
}
}

protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.3.0'
}
plugins {
javalite {
artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0'
}
grpc {
artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
}
}
generateProtoTasks {
all().each { task ->
task.plugins {
javalite {}
grpc {
// Options added to --grpc_out
option 'lite'
}
}
}
}
}

dependencies {
// Support Libraries
implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
implementation "com.android.support:recyclerview-v7:$supportLibraryVersion"

// gRPC
implementation "io.grpc:grpc-okhttp:$grpcVersion"
implementation "io.grpc:grpc-protobuf-lite:$grpcVersion"
implementation "io.grpc:grpc-stub:$grpcVersion"
implementation 'javax.annotation:javax.annotation-api:1.3'
protobuf 'com.google.protobuf:protobuf-java:3.4.0'
protobuf 'com.google.api.grpc:googleapis-common-protos:0.0.3'

// OAuth2 for Google API
implementation('com.google.auth:google-auth-library-oauth2-http:0.9.0') {
exclude module: 'httpclient'
}

// Test
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.19.0'
androidTestImplementation 'org.mockito:mockito-android:2.19.0'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

task copySecretKey(type: Copy) {
File secretKey = file "$System.env.GOOGLE_APPLICATION_CREDENTIALS"
from secretKey.getParent()
include secretKey.getName()
into 'src/main/res/raw'
rename secretKey.getName(), "credential.json"
}
preBuild.dependsOn(copySecretKey)
24 changes: 24 additions & 0 deletions dialogflow/Dialogflow/app/lint.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Google Inc. All Rights Reserved.

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.
-->
<lint>
<issue id="InvalidPackage">
<ignore regexp="grpc-core-1.5.0.jar" />
</issue>
<issue id="TrustAllX509TrustManager">
<ignore regexp="SslUtils" />
</issue>
</lint>
23 changes: 23 additions & 0 deletions dialogflow/Dialogflow/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2017 Google Inc. All Rights Reserved.
#
# 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.

# Suppress warnings from gRPC dependencies
-dontwarn com.google.common.**
-dontwarn com.google.api.client.**
-dontwarn com.google.protobuf.**
-dontwarn io.grpc.**
-dontwarn okio.**
-dontwarn com.google.errorprone.annotations.**
-keep class io.grpc.internal.DnsNameResolveProvider
-keep class io.grpc.okhttp.OkHttpChannelProvider
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2017 Google Inc. All Rights Reserved.
*
* 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.android.dialogflow;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.replaceText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

import static com.google.cloud.android.dialogflow.TestUtils.hasDirection;

import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;

import android.Manifest;
import android.support.test.filters.LargeTest;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.rule.GrantPermissionRule;
import android.support.test.runner.AndroidJUnit4;

import com.google.cloud.android.dialogflow.api.DialogflowService;
import com.google.cloud.android.dialogflow.api.Utterance;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;


@RunWith(AndroidJUnit4.class)
public class MainActivityTest {

@Rule
public ActivityTestRule<MainActivity> activityRule =
new ActivityTestRule<>(MainActivity.class);

@Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(Manifest.permission.RECORD_AUDIO);

private DialogflowService.Listener mListener;

@Before
public void setUp() {
final DialogflowService service = activityRule.getActivity().mDialogflowService;
mListener = mock(DialogflowService.Listener.class);
service.addListener(mListener);
}

@After
public void tearDown() {
activityRule.getActivity().mDialogflowService.removeListener(mListener);
}

@Test
@MediumTest
public void initialState() {
onView(withId(R.id.text)).check(matches(isDisplayed()));
onView(withId(R.id.toggle)).check(matches(isDisplayed()));
onView(withId(R.id.history)).check(matches(isDisplayed()));
}

@Test
@LargeTest
public void detectIntentByText() {
verify(mListener, timeout(TestUtils.API_TIMEOUT_MILLIS)).onApiReady();
onView(withId(R.id.text)).perform(replaceText("MainActivityTest"));
onView(withId(R.id.toggle)).perform(click());
verify(mListener)
.onNewUtterance(eq(new Utterance(Utterance.OUTGOING, "MainActivityTest")));
onView(withId(R.id.history)).check(matches(hasDescendant(withText("MainActivityTest"))));
verify(mListener, timeout(TestUtils.API_TIMEOUT_MILLIS))
.onNewUtterance(argThat(hasDirection(Utterance.INCOMING)));
}

@Test
@LargeTest
public void switchToVoice() {
verify(mListener, timeout(TestUtils.API_TIMEOUT_MILLIS)).onApiReady();
onView(withId(R.id.toggle)).perform(click());
onView(withId(R.id.indicator)).check(matches(isDisplayed()));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright 2017 Google Inc. All Rights Reserved.
*
* 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.android.dialogflow;

import com.google.cloud.android.dialogflow.api.Utterance;

import org.mockito.ArgumentMatcher;


public class TestUtils {

/** Timeout limit for API calls. */
public static final int API_TIMEOUT_MILLIS = 10000;

private TestUtils() {
}

/**
* Matches an {@link Utterance} with the specified {@code direction}.
*
* @param direction The direction. Either {@link Utterance#INCOMING} or {@link
* Utterance#OUTGOING}.
* @return The matcher.
*/
public static ArgumentMatcher<Utterance> hasDirection(final int direction) {
return new ArgumentMatcher<Utterance>() {
@Override
public boolean matches(Utterance utterance) {
return utterance.direction == direction;
}
};
}

}
Loading