1 /* 2 * Copyright 2016 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.routeguideexample; 18 19 import android.content.Context; 20 import android.os.AsyncTask; 21 import android.os.Bundle; 22 import android.support.v7.app.AppCompatActivity; 23 import android.text.TextUtils; 24 import android.text.method.ScrollingMovementMethod; 25 import android.view.View; 26 import android.view.inputmethod.InputMethodManager; 27 import android.widget.Button; 28 import android.widget.EditText; 29 import android.widget.TextView; 30 import io.grpc.ManagedChannel; 31 import io.grpc.ManagedChannelBuilder; 32 import io.grpc.StatusRuntimeException; 33 import io.grpc.routeguideexample.RouteGuideGrpc.RouteGuideBlockingStub; 34 import io.grpc.routeguideexample.RouteGuideGrpc.RouteGuideStub; 35 import io.grpc.stub.StreamObserver; 36 import java.io.PrintWriter; 37 import java.io.StringWriter; 38 import java.lang.ref.WeakReference; 39 import java.text.MessageFormat; 40 import java.util.ArrayList; 41 import java.util.Iterator; 42 import java.util.List; 43 import java.util.Random; 44 import java.util.concurrent.CountDownLatch; 45 import java.util.concurrent.TimeUnit; 46 47 public class RouteGuideActivity extends AppCompatActivity { 48 private EditText hostEdit; 49 private EditText portEdit; 50 private Button startRouteGuideButton; 51 private Button exitRouteGuideButton; 52 private Button getFeatureButton; 53 private Button listFeaturesButton; 54 private Button recordRouteButton; 55 private Button routeChatButton; 56 private TextView resultText; 57 private ManagedChannel channel; 58 59 @Override onCreate(Bundle savedInstanceState)60 protected void onCreate(Bundle savedInstanceState) { 61 super.onCreate(savedInstanceState); 62 setContentView(R.layout.activity_routeguide); 63 hostEdit = (EditText) findViewById(R.id.host_edit_text); 64 portEdit = (EditText) findViewById(R.id.port_edit_text); 65 startRouteGuideButton = (Button) findViewById(R.id.start_route_guide_button); 66 exitRouteGuideButton = (Button) findViewById(R.id.exit_route_guide_button); 67 getFeatureButton = (Button) findViewById(R.id.get_feature_button); 68 listFeaturesButton = (Button) findViewById(R.id.list_features_button); 69 recordRouteButton = (Button) findViewById(R.id.record_route_button); 70 routeChatButton = (Button) findViewById(R.id.route_chat_button); 71 resultText = (TextView) findViewById(R.id.result_text); 72 resultText.setMovementMethod(new ScrollingMovementMethod()); 73 disableButtons(); 74 } 75 startRouteGuide(View view)76 public void startRouteGuide(View view) { 77 String host = hostEdit.getText().toString(); 78 String portStr = portEdit.getText().toString(); 79 int port = TextUtils.isEmpty(portStr) ? 0 : Integer.valueOf(portStr); 80 ((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE)) 81 .hideSoftInputFromWindow(hostEdit.getWindowToken(), 0); 82 channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(); 83 hostEdit.setEnabled(false); 84 portEdit.setEnabled(false); 85 startRouteGuideButton.setEnabled(false); 86 enableButtons(); 87 } 88 exitRouteGuide(View view)89 public void exitRouteGuide(View view) { 90 channel.shutdown(); 91 disableButtons(); 92 hostEdit.setEnabled(true); 93 portEdit.setEnabled(true); 94 startRouteGuideButton.setEnabled(true); 95 } 96 getFeature(View view)97 public void getFeature(View view) { 98 setResultText(""); 99 disableButtons(); 100 new GrpcTask(new GetFeatureRunnable(), channel, this).execute(); 101 } 102 listFeatures(View view)103 public void listFeatures(View view) { 104 setResultText(""); 105 disableButtons(); 106 new GrpcTask(new ListFeaturesRunnable(), channel, this).execute(); 107 } 108 recordRoute(View view)109 public void recordRoute(View view) { 110 setResultText(""); 111 disableButtons(); 112 new GrpcTask(new RecordRouteRunnable(), channel, this).execute(); 113 } 114 routeChat(View view)115 public void routeChat(View view) { 116 setResultText(""); 117 disableButtons(); 118 new GrpcTask(new RouteChatRunnable(), channel, this).execute(); 119 } 120 setResultText(String text)121 private void setResultText(String text) { 122 resultText.setText(text); 123 } 124 disableButtons()125 private void disableButtons() { 126 getFeatureButton.setEnabled(false); 127 listFeaturesButton.setEnabled(false); 128 recordRouteButton.setEnabled(false); 129 routeChatButton.setEnabled(false); 130 exitRouteGuideButton.setEnabled(false); 131 } 132 enableButtons()133 private void enableButtons() { 134 exitRouteGuideButton.setEnabled(true); 135 getFeatureButton.setEnabled(true); 136 listFeaturesButton.setEnabled(true); 137 recordRouteButton.setEnabled(true); 138 routeChatButton.setEnabled(true); 139 } 140 141 private static class GrpcTask extends AsyncTask<Void, Void, String> { 142 private final GrpcRunnable grpcRunnable; 143 private final ManagedChannel channel; 144 private final WeakReference<RouteGuideActivity> activityReference; 145 GrpcTask(GrpcRunnable grpcRunnable, ManagedChannel channel, RouteGuideActivity activity)146 GrpcTask(GrpcRunnable grpcRunnable, ManagedChannel channel, RouteGuideActivity activity) { 147 this.grpcRunnable = grpcRunnable; 148 this.channel = channel; 149 this.activityReference = new WeakReference<RouteGuideActivity>(activity); 150 } 151 152 @Override doInBackground(Void... nothing)153 protected String doInBackground(Void... nothing) { 154 try { 155 String logs = 156 grpcRunnable.run( 157 RouteGuideGrpc.newBlockingStub(channel), RouteGuideGrpc.newStub(channel)); 158 return "Success!\n" + logs; 159 } catch (Exception e) { 160 StringWriter sw = new StringWriter(); 161 PrintWriter pw = new PrintWriter(sw); 162 e.printStackTrace(pw); 163 pw.flush(); 164 return "Failed... :\n" + sw; 165 } 166 } 167 168 @Override onPostExecute(String result)169 protected void onPostExecute(String result) { 170 RouteGuideActivity activity = activityReference.get(); 171 if (activity == null) { 172 return; 173 } 174 activity.setResultText(result); 175 activity.enableButtons(); 176 } 177 } 178 179 private interface GrpcRunnable { 180 /** Perform a grpcRunnable and return all the logs. */ run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub)181 String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub) throws Exception; 182 } 183 184 private static class GetFeatureRunnable implements GrpcRunnable { 185 @Override run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub)186 public String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub) 187 throws Exception { 188 return getFeature(409146138, -746188906, blockingStub); 189 } 190 191 /** Blocking unary call example. Calls getFeature and prints the response. */ getFeature(int lat, int lon, RouteGuideBlockingStub blockingStub)192 private String getFeature(int lat, int lon, RouteGuideBlockingStub blockingStub) 193 throws StatusRuntimeException { 194 StringBuffer logs = new StringBuffer(); 195 appendLogs(logs, "*** GetFeature: lat={0} lon={1}", lat, lon); 196 197 Point request = Point.newBuilder().setLatitude(lat).setLongitude(lon).build(); 198 199 Feature feature; 200 feature = blockingStub.getFeature(request); 201 if (RouteGuideUtil.exists(feature)) { 202 appendLogs( 203 logs, 204 "Found feature called \"{0}\" at {1}, {2}", 205 feature.getName(), 206 RouteGuideUtil.getLatitude(feature.getLocation()), 207 RouteGuideUtil.getLongitude(feature.getLocation())); 208 } else { 209 appendLogs( 210 logs, 211 "Found no feature at {0}, {1}", 212 RouteGuideUtil.getLatitude(feature.getLocation()), 213 RouteGuideUtil.getLongitude(feature.getLocation())); 214 } 215 return logs.toString(); 216 } 217 } 218 219 private static class ListFeaturesRunnable implements GrpcRunnable { 220 @Override run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub)221 public String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub) 222 throws Exception { 223 return listFeatures(400000000, -750000000, 420000000, -730000000, blockingStub); 224 } 225 226 /** 227 * Blocking server-streaming example. Calls listFeatures with a rectangle of interest. Prints 228 * each response feature as it arrives. 229 */ listFeatures( int lowLat, int lowLon, int hiLat, int hiLon, RouteGuideBlockingStub blockingStub)230 private String listFeatures( 231 int lowLat, int lowLon, int hiLat, int hiLon, RouteGuideBlockingStub blockingStub) 232 throws StatusRuntimeException { 233 StringBuffer logs = new StringBuffer("Result: "); 234 appendLogs( 235 logs, 236 "*** ListFeatures: lowLat={0} lowLon={1} hiLat={2} hiLon={3}", 237 lowLat, 238 lowLon, 239 hiLat, 240 hiLon); 241 242 Rectangle request = 243 Rectangle.newBuilder() 244 .setLo(Point.newBuilder().setLatitude(lowLat).setLongitude(lowLon).build()) 245 .setHi(Point.newBuilder().setLatitude(hiLat).setLongitude(hiLon).build()) 246 .build(); 247 Iterator<Feature> features; 248 features = blockingStub.listFeatures(request); 249 250 while (features.hasNext()) { 251 Feature feature = features.next(); 252 appendLogs(logs, feature.toString()); 253 } 254 return logs.toString(); 255 } 256 } 257 258 private static class RecordRouteRunnable implements GrpcRunnable { 259 private Throwable failed; 260 261 @Override run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub)262 public String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub) 263 throws Exception { 264 List<Point> points = new ArrayList<>(); 265 points.add(Point.newBuilder().setLatitude(407838351).setLongitude(-746143763).build()); 266 points.add(Point.newBuilder().setLatitude(408122808).setLongitude(-743999179).build()); 267 points.add(Point.newBuilder().setLatitude(413628156).setLongitude(-749015468).build()); 268 return recordRoute(points, 5, asyncStub); 269 } 270 271 /** 272 * Async client-streaming example. Sends {@code numPoints} randomly chosen points from {@code 273 * features} with a variable delay in between. Prints the statistics when they are sent from the 274 * server. 275 */ recordRoute(List<Point> points, int numPoints, RouteGuideStub asyncStub)276 private String recordRoute(List<Point> points, int numPoints, RouteGuideStub asyncStub) 277 throws InterruptedException, RuntimeException { 278 final StringBuffer logs = new StringBuffer(); 279 appendLogs(logs, "*** RecordRoute"); 280 281 final CountDownLatch finishLatch = new CountDownLatch(1); 282 StreamObserver<RouteSummary> responseObserver = 283 new StreamObserver<RouteSummary>() { 284 @Override 285 public void onNext(RouteSummary summary) { 286 appendLogs( 287 logs, 288 "Finished trip with {0} points. Passed {1} features. " 289 + "Travelled {2} meters. It took {3} seconds.", 290 summary.getPointCount(), 291 summary.getFeatureCount(), 292 summary.getDistance(), 293 summary.getElapsedTime()); 294 } 295 296 @Override 297 public void onError(Throwable t) { 298 failed = t; 299 finishLatch.countDown(); 300 } 301 302 @Override 303 public void onCompleted() { 304 appendLogs(logs, "Finished RecordRoute"); 305 finishLatch.countDown(); 306 } 307 }; 308 309 StreamObserver<Point> requestObserver = asyncStub.recordRoute(responseObserver); 310 try { 311 // Send numPoints points randomly selected from the points list. 312 Random rand = new Random(); 313 for (int i = 0; i < numPoints; ++i) { 314 int index = rand.nextInt(points.size()); 315 Point point = points.get(index); 316 appendLogs( 317 logs, 318 "Visiting point {0}, {1}", 319 RouteGuideUtil.getLatitude(point), 320 RouteGuideUtil.getLongitude(point)); 321 requestObserver.onNext(point); 322 // Sleep for a bit before sending the next one. 323 Thread.sleep(rand.nextInt(1000) + 500); 324 if (finishLatch.getCount() == 0) { 325 // RPC completed or errored before we finished sending. 326 // Sending further requests won't error, but they will just be thrown away. 327 break; 328 } 329 } 330 } catch (RuntimeException e) { 331 // Cancel RPC 332 requestObserver.onError(e); 333 throw e; 334 } 335 // Mark the end of requests 336 requestObserver.onCompleted(); 337 338 // Receiving happens asynchronously 339 if (!finishLatch.await(1, TimeUnit.MINUTES)) { 340 throw new RuntimeException( 341 "Could not finish rpc within 1 minute, the server is likely down"); 342 } 343 344 if (failed != null) { 345 throw new RuntimeException(failed); 346 } 347 return logs.toString(); 348 } 349 } 350 351 private static class RouteChatRunnable implements GrpcRunnable { 352 private Throwable failed; 353 354 @Override run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub)355 public String run(RouteGuideBlockingStub blockingStub, RouteGuideStub asyncStub) 356 throws Exception { 357 return routeChat(asyncStub); 358 } 359 360 /** 361 * Bi-directional example, which can only be asynchronous. Send some chat messages, and print 362 * any chat messages that are sent from the server. 363 */ routeChat(RouteGuideStub asyncStub)364 private String routeChat(RouteGuideStub asyncStub) 365 throws InterruptedException, RuntimeException { 366 final StringBuffer logs = new StringBuffer(); 367 appendLogs(logs, "*** RouteChat"); 368 final CountDownLatch finishLatch = new CountDownLatch(1); 369 StreamObserver<RouteNote> requestObserver = 370 asyncStub.routeChat( 371 new StreamObserver<RouteNote>() { 372 @Override 373 public void onNext(RouteNote note) { 374 appendLogs( 375 logs, 376 "Got message \"{0}\" at {1}, {2}", 377 note.getMessage(), 378 note.getLocation().getLatitude(), 379 note.getLocation().getLongitude()); 380 } 381 382 @Override 383 public void onError(Throwable t) { 384 failed = t; 385 finishLatch.countDown(); 386 } 387 388 @Override 389 public void onCompleted() { 390 appendLogs(logs, "Finished RouteChat"); 391 finishLatch.countDown(); 392 } 393 }); 394 395 try { 396 RouteNote[] requests = { 397 newNote("First message", 0, 0), 398 newNote("Second message", 0, 1), 399 newNote("Third message", 1, 0), 400 newNote("Fourth message", 1, 1) 401 }; 402 403 for (RouteNote request : requests) { 404 appendLogs( 405 logs, 406 "Sending message \"{0}\" at {1}, {2}", 407 request.getMessage(), 408 request.getLocation().getLatitude(), 409 request.getLocation().getLongitude()); 410 requestObserver.onNext(request); 411 } 412 } catch (RuntimeException e) { 413 // Cancel RPC 414 requestObserver.onError(e); 415 throw e; 416 } 417 // Mark the end of requests 418 requestObserver.onCompleted(); 419 420 // Receiving happens asynchronously 421 if (!finishLatch.await(1, TimeUnit.MINUTES)) { 422 throw new RuntimeException( 423 "Could not finish rpc within 1 minute, the server is likely down"); 424 } 425 426 if (failed != null) { 427 throw new RuntimeException(failed); 428 } 429 430 return logs.toString(); 431 } 432 } 433 appendLogs(StringBuffer logs, String msg, Object... params)434 private static void appendLogs(StringBuffer logs, String msg, Object... params) { 435 if (params.length > 0) { 436 logs.append(MessageFormat.format(msg, params)); 437 } else { 438 logs.append(msg); 439 } 440 logs.append("\n"); 441 } 442 newNote(String message, int lat, int lon)443 private static RouteNote newNote(String message, int lat, int lon) { 444 return RouteNote.newBuilder() 445 .setMessage(message) 446 .setLocation(Point.newBuilder().setLatitude(lat).setLongitude(lon).build()) 447 .build(); 448 } 449 } 450