/*
* Copyright 2016, Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package io.grpc.routeguideexample;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import io.grpc.routeguideexample.RouteGuideGrpc.RouteGuideBlockingStub;
import io.grpc.routeguideexample.RouteGuideGrpc.RouteGuideStub;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
public class RouteGuideActivity extends AppCompatActivity {
private static final Logger logger = Logger.getLogger(RouteGuideActivity.class.getName());
private EditText mHostEdit;
private EditText mPortEdit;
private Button mStartRouteGuideButton;
private Button mExitRouteGuideButton;
private Button mGetFeatureButton;
private Button mListFeaturesButton;
private Button mRecordRouteButton;
private Button mRouteChatButton;
private TextView mResultText;
private ManagedChannel mChannel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_routeguide);
mHostEdit = (EditText) findViewById(R.id.host_edit_text);
mPortEdit = (EditText) findViewById(R.id.port_edit_text);
mStartRouteGuideButton = (Button) findViewById(R.id.start_route_guide_button);
mExitRouteGuideButton = (Button) findViewById(R.id.exit_route_guide_button);
mGetFeatureButton = (Button) findViewById(R.id.get_feature_button);
mListFeaturesButton = (Button) findViewById(R.id.list_features_button);
mRecordRouteButton = (Button) findViewById(R.id.record_route_button);
mRouteChatButton = (Button) findViewById(R.id.route_chat_button);
mResultText = (TextView) findViewById(R.id.result_text);
mResultText.setMovementMethod(new ScrollingMovementMethod());
disableButtons();
}
public void startRouteGuide(View view) {
String host = mHostEdit.getText().toString();
String portStr = mPortEdit.getText().toString();
int port = TextUtils.isEmpty(portStr) ? 0 : Integer.valueOf(portStr);
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
.hideSoftInputFromWindow(mHostEdit.getWindowToken(), 0);
mChannel = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true).build();
mHostEdit.setEnabled(false);
mPortEdit.setEnabled(false);
mStartRouteGuideButton.setEnabled(false);
enableButtons();
}
public void exitRouteGuide(View view) {
mChannel.shutdown();
disableButtons();
mHostEdit.setEnabled(true);
mPortEdit.setEnabled(true);
mStartRouteGuideButton.setEnabled(true);
}
public void getFeature(View view) {
new GrpcTask(new GetFeatureRunnable()).execute();
}
public void listFeatures(View view) {
new GrpcTask(new ListFeaturesRunnable()).execute();
}
public void recordRoute(View view) {
new GrpcTask(new RecordRouteRunnable()).execute();
}
public void routeChat(View view) {
new GrpcTask(new RouteChatRunnable()).execute();
}
private void disableButtons() {
mGetFeatureButton.setEnabled(false);
mListFeaturesButton.setEnabled(false);
mRecordRouteButton.setEnabled(false);
mRouteChatButton.setEnabled(false);
mExitRouteGuideButton.setEnabled(false);
}
private void enableButtons() {
mExitRouteGuideButton.setEnabled(true);
mGetFeatureButton.setEnabled(true);
mListFeaturesButton.setEnabled(true);
mRecordRouteButton.setEnabled(true);
mRouteChatButton.setEnabled(true);
}
private class GrpcTask extends AsyncTask<Void, Void, String> {
private final GrpcRunnable mGrpc;
GrpcTask(GrpcRunnable grpc) {
this.mGrpc = grpc;
}
@Override
protected void onPreExecute() {
mResultText.setText("");
disableButtons();
}
@Override
protected String doInBackground(Void... nothing) {
try {
String logs = mGrpc.run(RouteGuideGrpc.newBlockingStub(mChannel),
RouteGuideGrpc.newStub(mChannel));
return "Success!" + System.lineSeparator() + logs;
} catch (Exception e) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
e.printStackTrace(pw);
pw.flush();
return "Failed... : " + System.lineSeparator() + sw;
}
}
@Override
protected void onPostExecute(String result) {
mResultText.setText(result);
enableButtons();
}
}
private interface GrpcRunnable {
/**
* Perform a grpc and return all the logs.
*/
String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub) throws Exception;
}
private class GetFeatureRunnable implements GrpcRunnable {
@Override
public String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub)
throws Exception {
return getFeature(409146138, -746188906, blockingStub);
}
/**
* Blocking unary call example. Calls getFeature and prints the response.
*/
private String getFeature(int lat, int lon, RouteGuideBlockingStub blockingStub)
throws StatusRuntimeException {
StringBuffer logs = new StringBuffer();
appendLogs(logs, "*** GetFeature: lat={0} lon={1}", lat, lon);
Point request = Point.newBuilder().setLatitude(lat).setLongitude(lon).build();
Feature feature;
feature = blockingStub.getFeature(request);
if (RouteGuideUtil.exists(feature)) {
appendLogs(logs, "Found feature called \"{0}\" at {1}, {2}",
feature.getName(),
RouteGuideUtil.getLatitude(feature.getLocation()),
RouteGuideUtil.getLongitude(feature.getLocation()));
} else {
appendLogs(logs, "Found no feature at {0}, {1}",
RouteGuideUtil.getLatitude(feature.getLocation()),
RouteGuideUtil.getLongitude(feature.getLocation()));
}
return logs.toString();
}
}
private class ListFeaturesRunnable implements GrpcRunnable {
@Override
public String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub)
throws Exception {
return listFeatures(400000000, -750000000, 420000000, -730000000, blockingStub);
}
/**
* Blocking server-streaming example. Calls listFeatures with a rectangle of interest.
* Prints each response feature as it arrives.
*/
private String listFeatures(int lowLat, int lowLon, int hiLat, int hiLon,
RouteGuideBlockingStub blockingStub) throws StatusRuntimeException {
StringBuffer logs = new StringBuffer("Result: ");
appendLogs(logs, "*** ListFeatures: lowLat={0} lowLon={1} hiLat={2} hiLon={3}",
lowLat, lowLon, hiLat, hiLon);
Rectangle request = Rectangle.newBuilder()
.setLo(Point.newBuilder().setLatitude(lowLat).setLongitude(lowLon).build())
.setHi(Point.newBuilder().setLatitude(hiLat).setLongitude(hiLon).build())
.build();
Iterator<Feature> features;
features = blockingStub.listFeatures(request);
while (features.hasNext()) {
Feature feature = features.next();
appendLogs(logs, feature.toString());
}
return logs.toString();
}
}
private class RecordRouteRunnable implements GrpcRunnable {
private Throwable failed;
@Override
public String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub)
throws Exception {
List<Point> points = new ArrayList<Point>();
points.add(Point.newBuilder()
.setLatitude(407838351).setLongitude(-746143763).build());
points.add(Point.newBuilder()
.setLatitude(408122808).setLongitude(-743999179).build());
points.add(Point.newBuilder()
.setLatitude(413628156).setLongitude(-749015468).build());
return recordRoute(points, 5, asyncStub);
}
/**
* Async client-streaming example. Sends {@code numPoints} randomly chosen points from
* {@code features} with a variable delay in between. Prints the statistics when they are
* sent from the server.
*/
private String recordRoute(List<Point> points, int numPoints, RouteGuideStub asyncStub)
throws InterruptedException, RuntimeException {
final StringBuffer logs = new StringBuffer();
appendLogs(logs, "*** RecordRoute");
final CountDownLatch finishLatch = new CountDownLatch(1);
StreamObserver<RouteSummary> responseObserver = new StreamObserver<RouteSummary>() {
@Override
public void onNext(RouteSummary summary) {
appendLogs(logs, "Finished trip with {0} points. Passed {1} features. "
+ "Travelled {2} meters. It took {3} seconds.", summary.getPointCount(),
summary.getFeatureCount(), summary.getDistance(),
summary.getElapsedTime());
}
@Override
public void onError(Throwable t) {
failed = t;
finishLatch.countDown();
}
@Override
public void onCompleted() {
appendLogs(logs, "Finished RecordRoute");
finishLatch.countDown();
}
};
StreamObserver<Point> requestObserver = asyncStub.recordRoute(responseObserver);
try {
// Send numPoints points randomly selected from the points list.
Random rand = new Random();
for (int i = 0; i < numPoints; ++i) {
int index = rand.nextInt(points.size());
Point point = points.get(index);
appendLogs(logs, "Visiting point {0}, {1}", RouteGuideUtil.getLatitude(point),
RouteGuideUtil.getLongitude(point));
requestObserver.onNext(point);
// Sleep for a bit before sending the next one.
Thread.sleep(rand.nextInt(1000) + 500);
if (finishLatch.getCount() == 0) {
// RPC completed or errored before we finished sending.
// Sending further requests won't error, but they will just be thrown away.
break;
}
}
} catch (RuntimeException e) {
// Cancel RPC
requestObserver.onError(e);
throw e;
}
// Mark the end of requests
requestObserver.onCompleted();
// Receiving happens asynchronously
if (!finishLatch.await(1, TimeUnit.MINUTES)) {
throw new RuntimeException(
"Could not finish rpc within 1 minute, the server is likely down");
}
if (failed != null) {
throw new RuntimeException(failed);
}
return logs.toString();
}
}
private class RouteChatRunnable implements GrpcRunnable {
private Throwable failed;
@Override
public String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub)
throws Exception {
return routeChat(asyncStub);
}
/**
* Bi-directional example, which can only be asynchronous. Send some chat messages, and
* print any chat messages that are sent from the server.
*/
private String routeChat(RouteGuideStub asyncStub) throws InterruptedException,
RuntimeException {
final StringBuffer logs = new StringBuffer();
appendLogs(logs, "*** RouteChat");
final CountDownLatch finishLatch = new CountDownLatch(1);
StreamObserver<RouteNote> requestObserver =
asyncStub.routeChat(new StreamObserver<RouteNote>() {
@Override
public void onNext(RouteNote note) {
appendLogs(logs, "Got message \"{0}\" at {1}, {2}", note.getMessage(),
note.getLocation().getLatitude(),
note.getLocation().getLongitude());
}
@Override
public void onError(Throwable t) {
failed = t;
finishLatch.countDown();
}
@Override
public void onCompleted() {
appendLogs(logs,"Finished RouteChat");
finishLatch.countDown();
}
});
try {
RouteNote[] requests =
{newNote("First message", 0, 0), newNote("Second message", 0, 1),
newNote("Third message", 1, 0), newNote("Fourth message", 1, 1)};
for (RouteNote request : requests) {
appendLogs(logs, "Sending message \"{0}\" at {1}, {2}", request.getMessage(),
request.getLocation().getLatitude(),
request.getLocation().getLongitude());
requestObserver.onNext(request);
}
} catch (RuntimeException e) {
// Cancel RPC
requestObserver.onError(e);
throw e;
}
// Mark the end of requests
requestObserver.onCompleted();
// Receiving happens asynchronously
if (!finishLatch.await(1, TimeUnit.MINUTES)) {
throw new RuntimeException(
"Could not finish rpc within 1 minute, the server is likely down");
}
if (failed != null) {
throw new RuntimeException(failed);
}
return logs.toString();
}
}
private static void appendLogs(StringBuffer logs, String msg, Object... params) {
if (params.length > 0) {
logs.append(MessageFormat.format(msg, params));
} else {
logs.append(msg);
}
logs.append(System.lineSeparator());
}
private static RouteNote newNote(String message, int lat, int lon) {
return RouteNote.newBuilder().setMessage(message)
.setLocation(Point.newBuilder().setLatitude(lat).setLongitude(lon).build()).build();
}
}