AI2 Component  (Version nb184)
GameClient.java
Go to the documentation of this file.
1 // -*- mode: java; c-basic-offset: 2; -*-
2 // Copyright 2009-2011 Google, All Rights reserved
3 // Copyright 2011-2012 MIT, All rights reserved
4 // Released under the Apache License, Version 2.0
5 // http://www.apache.org/licenses/LICENSE-2.0
6 
7 package com.google.appinventor.components.runtime;
8 
29 
30 import android.app.Activity;
31 import android.os.Handler;
32 import android.util.Log;
33 
34 import org.apache.http.NameValuePair;
35 import org.apache.http.message.BasicNameValuePair;
36 import org.json.JSONArray;
37 import org.json.JSONException;
38 import org.json.JSONObject;
39 
40 import java.util.ArrayList;
41 import java.util.List;
42 
78 @DesignerComponent(version = YaVersion.GAMECLIENT_COMPONENT_VERSION,
79  description = "Provides a way for applications to communicate with online game servers",
80  category = ComponentCategory.INTERNAL, // moved to internal until fully tested
81  nonVisible = true,
82  iconName = "images/gameClient.png")
83 @SimpleObject
84 @UsesPermissions(
85  permissionNames = "android.permission.INTERNET, " +
86  "com.google.android.googleapps.permission.GOOGLE_AUTH")
89 
90  private static final String LOG_TAG = "GameClient";
91 
92  // Parameter keys
93  private static final String GAME_ID_KEY = "gid";
94  private static final String INSTANCE_ID_KEY = "iid";
95  private static final String PLAYER_ID_KEY = "pid";
96  private static final String INVITEE_KEY = "inv";
97  private static final String LEADER_KEY = "leader";
98  private static final String COUNT_KEY = "count";
99  private static final String TYPE_KEY = "type";
100  private static final String INSTANCE_PUBLIC_KEY = "makepublic";
101  private static final String MESSAGE_RECIPIENTS_KEY = "mrec";
102  private static final String MESSAGE_CONTENT_KEY = "contents";
103  private static final String MESSAGE_TIME_KEY = "mtime";
104  private static final String MESSAGE_SENDER_KEY = "msender";
105  private static final String COMMAND_TYPE_KEY = "command";
106  private static final String COMMAND_ARGUMENTS_KEY = "args";
107  private static final String SERVER_RETURN_VALUE_KEY = "response";
108  private static final String MESSAGES_LIST_KEY = "messages";
109  private static final String ERROR_RESPONSE_KEY = "e";
110  private static final String PUBLIC_LIST_KEY = "public";
111  private static final String JOINED_LIST_KEY = "joined";
112  private static final String INVITED_LIST_KEY = "invited";
113  private static final String PLAYERS_LIST_KEY = "players";
114 
115  // Command keys
116  private static final String GET_INSTANCE_LISTS_COMMAND = "getinstancelists";
117  private static final String GET_MESSAGES_COMMAND = "messages";
118  private static final String INVITE_COMMAND = "invite";
119  private static final String JOIN_INSTANCE_COMMAND = "joininstance";
120  private static final String LEAVE_INSTANCE_COMMAND = "leaveinstance";
121  private static final String NEW_INSTANCE_COMMAND = "newinstance";
122  private static final String NEW_MESSAGE_COMMAND = "newmessage";
123  private static final String SERVER_COMMAND = "servercommand";
124  private static final String SET_LEADER_COMMAND = "setleader";
125 
126  // URL for accessing the game server
127  private String serviceUrl;
128  private String gameId;
129  private GameInstance instance;
130  private Handler androidUIHandler;
131  private Activity activityContext;
132 
133  private String userEmailAddress = "";
134 
135  // Game instances in the current GameId that this player has joined
136  private List<String> joinedInstances;
137  // Game instances to which this player has been invited
138  private List<String> invitedInstances;
139  // Game instances which have been made public.
140  private List<String> publicInstances;
141 
147  public GameClient(ComponentContainer container) {
148  super(container.$form());
149  // Note that although this is creating a new Handler there is
150  // only one UI thread in an Android app and posting to this
151  // handler queues up a Runnable for execution on that thread.
152  androidUIHandler = new Handler();
153  activityContext = container.$context();
154  form.registerForOnResume(this);
155  form.registerForOnStop(this);
156  gameId = "";
157  instance = new GameInstance("");
158  joinedInstances = Lists.newArrayList();
159  invitedInstances = Lists.newArrayList();
160  publicInstances = Lists.newArrayList();
161  serviceUrl = "http://appinvgameserver.appspot.com";
162 
163  // This needs to be done in a separate thread since it uses
164  // a blocking service to complete and will cause the UI to hang
165  // if it happens in the constructor.
166  /*
167  * Remove this code until we fix LoginServiceUtil to work in later
168  * versions of the android SDK.
169  AsynchUtil.runAsynchronously(new Runnable() {
170  @Override
171  public void run() {
172  userEmailAddress = LoginServiceUtil.getPhoneEmailAddress(activityContext);
173  if (!userEmailAddress.equals("")) {
174  UserEmailAddressSet(userEmailAddress);
175  }
176  }
177  });
178  */
179  }
180 
181 
182  //----------------------------------------------------------------
183  // Properties
184 
190  description = "The game name for this application. " +
191  "The same game ID can have one or more game instances.",
192  category = PropertyCategory.BEHAVIOR)
193  public String GameId() {
194  return gameId;
195  }
196 
202  // Only exposed in the designer to enforce that each GameClient
203  // instance should be made for a single GameId.
206  defaultValue = "")
207  public void GameId(String id) {
208  this.gameId = id;
209  }
210 
216  description = "The game instance id. Taken together," +
217  "the game ID and the instance ID uniquely identify the game.",
218  category = PropertyCategory.BEHAVIOR)
219  public String InstanceId() {
220  return instance.getInstanceId();
221  }
222 
229  description = "The set of game instances to which this player has been " +
230  "invited but has not yet joined. To ensure current values are " +
231  "returned, first invoke GetInstanceLists.",
232  category = PropertyCategory.BEHAVIOR)
233  public List<String> InvitedInstances() {
234  return invitedInstances;
235  }
236 
243  description = "The set of game instances in which this player is " +
244  "participating. To ensure current values are returned, first " +
245  "invoke GetInstanceLists.",
246  category = PropertyCategory.BEHAVIOR)
247  public List<String> JoinedInstances() {
248  return joinedInstances;
249  }
250 
260  description = "The game's leader. At any time, each game instance has " +
261  "only one leader, but the leader may change with time. " +
262  "Initially, the leader is the game instance creator. Application " +
263  "writers determine special properties of the leader. The leader " +
264  "value is updated each time a successful communication is made " +
265  "with the server.",
266  category = PropertyCategory.BEHAVIOR)
267  public String Leader() {
268  return instance.getLeader();
269  }
270 
278  description = "The current set of players for this game instance. Each " +
279  "player is designated by an email address, which is a string. The " +
280  "list of players is updated each time a successful communication " +
281  "is made with the game server.",
282  category = PropertyCategory.BEHAVIOR)
283  public List<String> Players() {
284  return instance.getPlayers();
285  }
286 
293  description = "The set of game instances that have been marked public. " +
294  "To ensure current values are returned, first " +
295  "invoke {@link #GetInstanceLists}. ",
296  category = PropertyCategory.BEHAVIOR)
297  public List<String> PublicInstances() {
298  return publicInstances;
299  }
300 
305  description = "The URL of the game server.",
306  category = PropertyCategory.BEHAVIOR)
307  public String ServiceUrl() {
308  return serviceUrl;
309  }
310 
318  defaultValue = "http://appinvgameserver.appspot.com")
319  @SimpleProperty(userVisible = false)
320  public void ServiceURL(String url){
321  if (url.endsWith("/")) {
322  this.serviceUrl = url.substring(0, url.length() - 1);
323  } else {
324  this.serviceUrl = url;
325  }
326  }
327 
333  description = "The email address that is being used as the " +
334  "player id for this game client. At present, users " +
335  "must set this manually in oder to join a game. But " +
336  "this property will change in the future so that is set " +
337  "automatically, and users will not be able to change it.",
338  category = PropertyCategory.BEHAVIOR)
339 
340  public String UserEmailAddress() {
341  if (userEmailAddress.equals("")) {
342  Info("User email address is empty.");
343  }
344  return userEmailAddress;
345  }
346 
358  public void UserEmailAddress(String emailAddress) {
359  userEmailAddress = emailAddress;
360  UserEmailAddressSet(emailAddress);
361  }
362 
363  //----------------------------------------------------------------
364  // Event Handlers
365 
374  @SimpleEvent(description = "Indicates that a function call completed.")
375  public void FunctionCompleted(final String functionName) {
376  androidUIHandler.post(new Runnable() {
377  public void run() {
378  Log.d(LOG_TAG, "Request completed: " + functionName);
379  EventDispatcher.dispatchEvent(GameClient.this, "FunctionCompleted", functionName);
380  }});
381  }
382 
386  public void Initialize() {
387  Log.d(LOG_TAG, "Initialize");
388  if (gameId.equals("")) {
389  throw new YailRuntimeError("Game Id must not be empty.", "GameClient Configuration Error.");
390  }
391  }
392 
402  @SimpleEvent(description = "Indicates that a new message has " +
403  "been received.")
404  public void GotMessage(final String type, final String sender, final List<Object> contents) {
405  Log.d(LOG_TAG, "Got message of type " + type);
406  androidUIHandler.post(new Runnable() {
407  public void run() {
408  EventDispatcher.dispatchEvent(GameClient.this, "GotMessage", type, sender, contents);
409  }});
410  }
411 
418  @SimpleEvent(description = "Indicates that the InstanceId " +
419  "property has changed as a result of calling " +
420  "MakeNewInstance or SetInstance.")
421  public void InstanceIdChanged(final String instanceId) {
422  Log.d(LOG_TAG, "Instance id changed to " + instanceId);
423  androidUIHandler.post(new Runnable() {
424  public void run() {
425  EventDispatcher.dispatchEvent(GameClient.this, "InstanceIdChanged", instanceId);
426  }});
427  }
428 
435  @SimpleEvent(
436  description = "Indicates that a user has been invited to " +
437  "this game instance.")
438  public void Invited(final String instanceId) {
439  Log.d(LOG_TAG, "Player invited to " + instanceId);
440  androidUIHandler.post(new Runnable() {
441  public void run() {
442  EventDispatcher.dispatchEvent(GameClient.this, "Invited", instanceId);
443  }});
444  }
445 
456  @SimpleEvent(description = "Indicates that this game has a new " +
457  "leader as specified through SetLeader")
458  public void NewLeader(final String playerId) {
459  androidUIHandler.post(new Runnable() {
460  public void run() {
461  Log.d(LOG_TAG, "Leader change to " + playerId);
462  EventDispatcher.dispatchEvent(GameClient.this, "NewLeader", playerId);
463  }});
464  }
465 
474  @SimpleEvent(description = "Indicates that a new instance was " +
475  "successfully created after calling MakeNewInstance.")
476  public void NewInstanceMade(final String instanceId) {
477  androidUIHandler.post(new Runnable() {
478  public void run() {
479  Log.d(LOG_TAG, "New instance made: " + instanceId);
480  EventDispatcher.dispatchEvent(GameClient.this, "NewInstanceMade", instanceId);
481  }});
482  }
483 
489  @SimpleEvent(description = "Indicates that a new player has " +
490  "joined this game instance.")
491  public void PlayerJoined(final String playerId) {
492  androidUIHandler.post(new Runnable() {
493  public void run() {
494  if (!playerId.equals(UserEmailAddress())) {
495  Log.d(LOG_TAG, "Player joined: " + playerId);
496  EventDispatcher.dispatchEvent(GameClient.this, "PlayerJoined", playerId);
497  }
498  }});
499  }
500 
506  @SimpleEvent(description = "Indicates that a player has left " +
507  "this game instance.")
508  public void PlayerLeft(final String playerId) {
509  androidUIHandler.post(new Runnable() {
510  public void run() {
511  Log.d(LOG_TAG, "Player left: " + playerId);
512  EventDispatcher.dispatchEvent(GameClient.this, "PlayerLeft", playerId);
513  }});
514  }
515 
522  @SimpleEvent(
523  description = "Indicates that a server command failed.")
524  public void ServerCommandFailure(final String command, final YailList arguments) {
525  androidUIHandler.post(new Runnable() {
526  public void run() {
527  Log.d(LOG_TAG, "Server command failed: " + command);
528  EventDispatcher.dispatchEvent(GameClient.this, "ServerCommandFailure", command, arguments);
529  }});
530  }
531 
541  @SimpleEvent(description = "Indicates that a server command " +
542  "returned successfully.")
543  public void ServerCommandSuccess(final String command, final List<Object> response) {
544  Log.d(LOG_TAG, command + " server command returned.");
545  androidUIHandler.post(new Runnable() {
546  public void run() {
548  "ServerCommandSuccess", command, response);
549  }});
550  }
551 
563  @SimpleEvent(description = "Indicates that the user email " +
564  "address has been set.")
565  public void UserEmailAddressSet(final String emailAddress) {
566  Log.d(LOG_TAG, "Email address set.");
567  androidUIHandler.post(new Runnable() {
568  public void run() {
569  EventDispatcher.dispatchEvent(GameClient.this, "UserEmailAddressSet", emailAddress);
570  }});
571  }
572 
573  //----------------------------------------------------------------
574  // Message events
575 
582  @SimpleEvent(description = "Indicates that something has " +
583  "occurred which the player should know about.")
584  public void Info(final String message) {
585  Log.d(LOG_TAG, "Info: " + message);
586  androidUIHandler.post(new Runnable() {
587  public void run() {
588  EventDispatcher.dispatchEvent(GameClient.this, "Info", message);
589  }});
590 
591  }
592 
601  @SimpleEvent(description = "Indicates that an error occurred " +
602  "while communicating with the web server.")
603  public void WebServiceError(final String functionName, final String message) {
604  Log.e(LOG_TAG, "WebServiceError: " + message);
605  androidUIHandler.post(new Runnable() {
606  public void run() {
607  EventDispatcher.dispatchEvent(GameClient.this, "WebServiceError", functionName, message);
608  }});
609  }
610 
611  //----------------------------------------------------------------
612  // Functions
613 
620  @SimpleFunction(description = "Updates the InstancesJoined and " +
621  "InstancesInvited lists. This procedure can be called " +
622  "before setting the InstanceId.")
623  public void GetInstanceLists() {
624  AsynchUtil.runAsynchronously(new Runnable() {
625  public void run() { postGetInstanceLists(); }});
626  }
627 
628  private void postGetInstanceLists() {
630  public void onSuccess(final JSONObject response) {
631  processInstanceLists(response);
632  FunctionCompleted("GetInstanceLists");
633  }
634  public void onFailure(final String message) {
635  WebServiceError("GetInstanceLists", "Failed to get up to date instance lists.");
636  }
637  };
638 
639  postCommandToGameServer(GET_INSTANCE_LISTS_COMMAND,
640  Lists.<NameValuePair>newArrayList(
641  new BasicNameValuePair(GAME_ID_KEY, GameId()),
642  new BasicNameValuePair(INSTANCE_ID_KEY, InstanceId()),
643  new BasicNameValuePair(PLAYER_ID_KEY, UserEmailAddress())),
644  readMessagesCallback);
645  }
646 
647  private void processInstanceLists(JSONObject instanceLists){
648  try {
649  joinedInstances = JsonUtil.getStringListFromJsonArray(instanceLists.
650  getJSONArray(JOINED_LIST_KEY));
651 
652  publicInstances = JsonUtil.getStringListFromJsonArray(instanceLists.
653  getJSONArray(PUBLIC_LIST_KEY));
654 
655  List<String> receivedInstancesInvited = JsonUtil.getStringListFromJsonArray(instanceLists.
656  getJSONArray(INVITED_LIST_KEY));
657 
658  if (!receivedInstancesInvited.equals(InvitedInstances())) {
659  List<String> oldList = invitedInstances;
660  invitedInstances = receivedInstancesInvited;
661  List<String> newInvites = new ArrayList<String>(receivedInstancesInvited);
662  newInvites.removeAll(oldList);
663 
664  for (final String instanceInvited : newInvites) {
665  Invited(instanceInvited);
666  }
667  }
668 
669  } catch (JSONException e) {
670  Log.w(LOG_TAG, e);
671  Info("Instance lists failed to parse.");
672  }
673  }
674 
705  @SimpleFunction(
706  description = "Retrieves messages of the specified type.")
707  public void GetMessages(final String type, final int count) {
708  AsynchUtil.runAsynchronously(new Runnable() {
709  public void run() { postGetMessages(type, count); }});
710  }
711 
712  private void postGetMessages(final String requestedType, final int count) {
714  public void onSuccess(final JSONObject result) {
715  try {
716  int count = result.getInt(COUNT_KEY);
717  JSONArray messages = result.getJSONArray(MESSAGES_LIST_KEY);
718  for (int i = 0; i < count; i++) {
719  JSONObject message = messages.getJSONObject(i);
720  String type = message.getString(TYPE_KEY);
721  String sender = message.getString(MESSAGE_SENDER_KEY);
722  String time = message.getString(MESSAGE_TIME_KEY);
723  List<Object> contents = JsonUtil.getListFromJsonArray(message.
724  getJSONArray(MESSAGE_CONTENT_KEY), true);
725  // Assumes that the server is going to return messages in
726  // chronological order.
727  if (requestedType.equals("")) {
728  instance.putMessageTime(requestedType, time);
729  }
730  instance.putMessageTime(type, time);
731  GotMessage(type, sender, contents);
732  }
733  } catch (JSONException e) {
734  Log.w(LOG_TAG, e);
735  Info("Failed to parse messages response.");
736  }
737  FunctionCompleted("GetMessages");
738  }
739 
740  public void onFailure(String message) {
741  WebServiceError("GetMessages", message);
742  }
743  };
744 
745  if (InstanceId().equals("")) {
746  Info("You must join an instance before attempting to fetch messages.");
747  return;
748  }
749 
750  postCommandToGameServer(GET_MESSAGES_COMMAND,
751  Lists.<NameValuePair>newArrayList(
752  new BasicNameValuePair(GAME_ID_KEY, GameId()),
753  new BasicNameValuePair(INSTANCE_ID_KEY, InstanceId()),
754  new BasicNameValuePair(PLAYER_ID_KEY, UserEmailAddress()),
755  new BasicNameValuePair(COUNT_KEY, Integer.toString(count)),
756  new BasicNameValuePair(MESSAGE_TIME_KEY, instance.getMessageTime(requestedType)),
757  new BasicNameValuePair(TYPE_KEY, requestedType)),
758  myCallback);
759  }
760 
774  @SimpleFunction(
775  description = "Invites a player to this game instance.")
776  public void Invite(final String playerEmail) {
777  AsynchUtil.runAsynchronously(new Runnable() {
778  public void run() { postInvite(playerEmail); }});
779  }
780 
781  private void postInvite(final String inviteeEmail) {
783  public void onSuccess(final JSONObject response) {
784  try {
785  String invitedPlayer = response.getString(INVITEE_KEY);
786 
787  if (invitedPlayer.equals("")) {
788  Info(invitedPlayer + " was already invited.");
789  } else {
790  Info("Successfully invited " + invitedPlayer + ".");
791  }
792  } catch (JSONException e) {
793  Log.w(LOG_TAG, e);
794  Info("Failed to parse invite player response.");
795  }
796  FunctionCompleted("Invite");
797  }
798  public void onFailure(final String message) {
799  WebServiceError("Invite", message);
800  }
801  };
802 
803  if (InstanceId().equals("")) {
804  Info("You must have joined an instance before you can invite new players.");
805  return;
806  }
807 
808  postCommandToGameServer(INVITE_COMMAND,
809  Lists.<NameValuePair>newArrayList(
810  new BasicNameValuePair(GAME_ID_KEY, GameId()),
811  new BasicNameValuePair(INSTANCE_ID_KEY, InstanceId()),
812  new BasicNameValuePair(PLAYER_ID_KEY, UserEmailAddress()),
813  new BasicNameValuePair(INVITEE_KEY, inviteeEmail)),
814  inviteCallback);
815  }
816 
831  @SimpleFunction(description = "Leaves the current instance.")
832  public void LeaveInstance() {
833  AsynchUtil.runAsynchronously(new Runnable() {
834  public void run() {
835  postLeaveInstance();
836  }
837  });
838  }
839 
840  private void postLeaveInstance() {
842  public void onSuccess(final JSONObject response) {
843  SetInstance("");
844  processInstanceLists(response);
845  FunctionCompleted("LeaveInstance");
846  }
847  public void onFailure(final String message) {
848  WebServiceError("LeaveInstance", message);
849  }
850  };
851 
852  postCommandToGameServer(LEAVE_INSTANCE_COMMAND,
853  Lists.<NameValuePair>newArrayList(
854  new BasicNameValuePair(GAME_ID_KEY, GameId()),
855  new BasicNameValuePair(INSTANCE_ID_KEY, InstanceId()),
856  new BasicNameValuePair(PLAYER_ID_KEY, UserEmailAddress())),
857  setInstanceCallback);
858  }
859 
881  @SimpleFunction(description = "Asks the server to create a new " +
882  "instance of this game.")
883  public void MakeNewInstance(final String instanceId, final boolean makePublic) {
884  AsynchUtil.runAsynchronously(new Runnable() {
885  public void run() { postMakeNewInstance(instanceId, makePublic); }});
886  }
887 
888  private void postMakeNewInstance(final String requestedInstanceId, final Boolean makePublic) {
890  public void onSuccess(final JSONObject response) {
891  processInstanceLists(response);
892  NewInstanceMade(InstanceId());
893  FunctionCompleted("MakeNewInstance");
894  }
895  public void onFailure(final String message) {
896  WebServiceError("MakeNewInstance", message);
897  }
898  };
899 
900  postCommandToGameServer(NEW_INSTANCE_COMMAND,
901  Lists.<NameValuePair>newArrayList(
902  new BasicNameValuePair(PLAYER_ID_KEY, UserEmailAddress()),
903  new BasicNameValuePair(GAME_ID_KEY, GameId()),
904  new BasicNameValuePair(INSTANCE_ID_KEY, requestedInstanceId),
905  new BasicNameValuePair(INSTANCE_PUBLIC_KEY, makePublic.toString())),
906  makeNewGameCallback, true);
907  }
908 
927  @SimpleFunction(description = "Sends a keyed message to all " +
928  "recipients in the recipients list. The message will " +
929  "consist of the contents list.")
930  public void SendMessage(final String type, final YailList recipients, final YailList contents) {
931  AsynchUtil.runAsynchronously(new Runnable() {
932  public void run() { postNewMessage(type, recipients, contents); }});
933  }
934 
935  private void postNewMessage(final String type, YailList recipients, YailList contents){
937  public void onSuccess(final JSONObject response) {
938  FunctionCompleted("SendMessage");
939  }
940  public void onFailure(final String message) {
941  WebServiceError("SendMessage", message);
942  }
943  };
944 
945  if (InstanceId().equals("")) {
946  Info("You must have joined an instance before you can send messages.");
947  return;
948  }
949 
950  postCommandToGameServer(NEW_MESSAGE_COMMAND,
951  Lists.<NameValuePair>newArrayList(
952  new BasicNameValuePair(GAME_ID_KEY, GameId()),
953  new BasicNameValuePair(INSTANCE_ID_KEY, InstanceId()),
954  new BasicNameValuePair(PLAYER_ID_KEY, UserEmailAddress()),
955  new BasicNameValuePair(TYPE_KEY, type),
956  new BasicNameValuePair(MESSAGE_RECIPIENTS_KEY, recipients.toJSONString()),
957  new BasicNameValuePair(MESSAGE_CONTENT_KEY, contents.toJSONString()),
958  new BasicNameValuePair(MESSAGE_TIME_KEY, instance.getMessageTime(type))),
959  myCallback);
960  }
961 
976  @SimpleFunction(description = "Sends the specified command to " +
977  "the game server.")
978  public void ServerCommand(final String command, final YailList arguments) {
979  AsynchUtil.runAsynchronously(new Runnable() {
980  public void run() { postServerCommand(command, arguments); }});
981  }
982 
983  private void postServerCommand(final String command, final YailList arguments){
985  public void onSuccess(final JSONObject result) {
986  try {
987  ServerCommandSuccess(command, JsonUtil.getListFromJsonArray(result.
988  getJSONArray(MESSAGE_CONTENT_KEY), true));
989  } catch (JSONException e) {
990  Log.w(LOG_TAG, e);
991  Info("Server command response failed to parse.");
992  }
993  FunctionCompleted("ServerCommand");
994  }
995 
996  public void onFailure(String message) {
997  ServerCommandFailure(command, arguments);
998  WebServiceError("ServerCommand", message);
999  }
1000  };
1001 
1002  Log.d(LOG_TAG, "Going to post " + command + " with args " + arguments);
1003  postCommandToGameServer(SERVER_COMMAND,
1004  Lists.<NameValuePair>newArrayList(
1005  new BasicNameValuePair(GAME_ID_KEY, GameId()),
1006  new BasicNameValuePair(INSTANCE_ID_KEY, InstanceId()),
1007  new BasicNameValuePair(PLAYER_ID_KEY, UserEmailAddress()),
1008  new BasicNameValuePair(COMMAND_TYPE_KEY, command),
1009  new BasicNameValuePair(COMMAND_ARGUMENTS_KEY, arguments.toJSONString())),
1010  myCallback);
1011  }
1012 
1019  @SimpleFunction(description = "Sets InstanceId and joins the " +
1020  "specified instance.")
1021  public void SetInstance(final String instanceId) {
1022  AsynchUtil.runAsynchronously(new Runnable() {
1023  public void run() {
1024  if (instanceId.equals("")) {
1025  Log.d(LOG_TAG, "Instance id set to empty string.");
1026  if (!InstanceId().equals("")) {
1027  instance = new GameInstance("");
1028  InstanceIdChanged("");
1029  FunctionCompleted("SetInstance");
1030  }
1031  } else {
1032  postSetInstance(instanceId);
1033  }
1034  }
1035  });
1036  }
1037 
1038  private void postSetInstance(String instanceId) {
1040  public void onSuccess(final JSONObject response) {
1041  processInstanceLists(response);
1042  FunctionCompleted("SetInstance");
1043  }
1044  public void onFailure(final String message) {
1045  WebServiceError("SetInstance", message);
1046  }
1047  };
1048 
1049  postCommandToGameServer(JOIN_INSTANCE_COMMAND,
1050  Lists.<NameValuePair>newArrayList(
1051  new BasicNameValuePair(GAME_ID_KEY, GameId()),
1052  new BasicNameValuePair(INSTANCE_ID_KEY, instanceId),
1053  new BasicNameValuePair(PLAYER_ID_KEY, UserEmailAddress())),
1054  setInstanceCallback, true);
1055  }
1056 
1071  @SimpleFunction(description = "Tells the server to set the " +
1072  "leader to playerId. Only the current leader may " +
1073  "successfully set a new leader.")
1074  public void SetLeader(final String playerEmail) {
1075  AsynchUtil.runAsynchronously(new Runnable() {
1076  public void run() { postSetLeader(playerEmail); }});
1077  }
1078 
1079  private void postSetLeader(final String newLeader) {
1081  public void onSuccess(final JSONObject response) {
1082  FunctionCompleted("SetLeader");
1083  }
1084  public void onFailure(final String message) {
1085  WebServiceError("SetLeader", message);
1086  }
1087  };
1088 
1089  if (InstanceId().equals("")) {
1090  Info("You must join an instance before attempting to set a leader.");
1091  return;
1092  }
1093 
1094  postCommandToGameServer(SET_LEADER_COMMAND,
1095  Lists.<NameValuePair>newArrayList(
1096  new BasicNameValuePair(GAME_ID_KEY, GameId()),
1097  new BasicNameValuePair(INSTANCE_ID_KEY, InstanceId()),
1098  new BasicNameValuePair(PLAYER_ID_KEY, UserEmailAddress()),
1099  new BasicNameValuePair(LEADER_KEY, newLeader)),
1100  setLeaderCallback);
1101  }
1102 
1103  //----------------------------------------------------------------
1104  // Activity Lifecycle Management
1105 
1111  public void onResume() {
1112  Log.d(LOG_TAG, "Activity Resumed.");
1113  }
1114 
1120  public void onStop() {
1121  Log.d(LOG_TAG, "Activity Stopped.");
1122  }
1123 
1124  //----------------------------------------------------------------
1125  // Utility Methods
1126 
1127  private void postCommandToGameServer(final String commandName,
1128  List<NameValuePair> params, final AsyncCallbackPair<JSONObject> callback) {
1129  postCommandToGameServer(commandName, params, callback, false);
1130  }
1131 
1132  private void postCommandToGameServer(final String commandName,
1133  final List<NameValuePair> params, final AsyncCallbackPair<JSONObject> callback,
1134  final boolean allowInstanceIdChange) {
1135  AsyncCallbackPair<JSONObject> thisCallback = new AsyncCallbackPair<JSONObject>() {
1136  public void onSuccess(JSONObject responseObject) {
1137  Log.d(LOG_TAG, "Received response for " + commandName + ": " + responseObject.toString());
1138 
1139  try {
1140  if (responseObject.getBoolean(ERROR_RESPONSE_KEY)) {
1141  callback.onFailure(responseObject.getString(SERVER_RETURN_VALUE_KEY));
1142  } else {
1143  String responseGameId = responseObject.getString(GAME_ID_KEY);
1144  if (!responseGameId.equals(GameId())) {
1145  Info("Incorrect game id in response: + " + responseGameId + ".");
1146  return;
1147  }
1148  String responseInstanceId = responseObject.getString(INSTANCE_ID_KEY);
1149  if (responseInstanceId.equals("")) {
1150  callback.onSuccess(responseObject.getJSONObject(SERVER_RETURN_VALUE_KEY));
1151  return;
1152  }
1153 
1154  if (responseInstanceId.equals(InstanceId())) {
1155  updateInstanceInfo(responseObject);
1156  } else {
1157  if (allowInstanceIdChange || InstanceId().equals("")) {
1158  instance = new GameInstance(responseInstanceId);
1159  updateInstanceInfo(responseObject);
1160  InstanceIdChanged(responseInstanceId);
1161  } else {
1162  Info("Ignored server response to " + commandName + " for incorrect instance " +
1163  responseInstanceId + ".");
1164  return;
1165  }
1166  }
1167  callback.onSuccess(responseObject.getJSONObject(SERVER_RETURN_VALUE_KEY));
1168  }
1169  } catch (JSONException e) {
1170  Log.w(LOG_TAG, e);
1171  callback.onFailure("Failed to parse JSON response to command " + commandName);
1172  }
1173  }
1174  public void onFailure(String failureMessage) {
1175  Log.d(LOG_TAG, "Posting to server failed for " + commandName + " with arguments " +
1176  params + "\n Failure message: " + failureMessage);
1177  callback.onFailure(failureMessage);
1178  }
1179  };
1180 
1181  WebServiceUtil.getInstance().postCommandReturningObject(ServiceUrl(), commandName, params,
1182  thisCallback);
1183  }
1184 
1185  private void updateInstanceInfo(JSONObject responseObject) throws JSONException {
1186  boolean newLeader = false;
1187  String leader = responseObject.getString(LEADER_KEY);
1188  List<String> receivedPlayers = JsonUtil.getStringListFromJsonArray(responseObject.
1189  getJSONArray(PLAYERS_LIST_KEY));
1190 
1191  if (!Leader().equals(leader)) {
1192  instance.setLeader(leader);
1193  newLeader = true;
1194  }
1195 
1196  PlayerListDelta playersDelta = instance.setPlayers(receivedPlayers);
1197  if (playersDelta != PlayerListDelta.NO_CHANGE) {
1198  for (final String player : playersDelta.getPlayersRemoved()) {
1199  PlayerLeft(player);
1200  }
1201  for (final String player : playersDelta.getPlayersAdded()) {
1202  PlayerJoined(player);
1203  }
1204  }
1205 
1206  if (newLeader) {
1207  NewLeader(Leader());
1208  }
1209  }
1210 }
com.google.appinventor.components.runtime.EventDispatcher
Definition: EventDispatcher.java:22
com.google.appinventor.components.runtime.util.YailList
Definition: YailList.java:26
com.google.appinventor.components.annotations.SimpleFunction
Definition: SimpleFunction.java:23
com.google.appinventor.components.runtime.GameClient.UserEmailAddress
void UserEmailAddress(String emailAddress)
Definition: GameClient.java:358
com.google.appinventor.components.runtime.util.JsonUtil.getListFromJsonArray
static List< Object > getListFromJsonArray(JSONArray jsonArray)
Definition: JsonUtil.java:76
com.google.appinventor.components.runtime.util
-*- mode: java; c-basic-offset: 2; -*-
Definition: AccountChooser.java:7
com.google.appinventor.components.runtime.util.PlayerListDelta
Definition: PlayerListDelta.java:17
com.google.appinventor.components.common.YaVersion
Definition: YaVersion.java:14
com.google.appinventor.components.runtime.GameClient
Definition: GameClient.java:87
com.google.appinventor.components.annotations.DesignerProperty
Definition: DesignerProperty.java:25
com.google.appinventor.components.common.PropertyTypeConstants.PROPERTY_TYPE_STRING
static final String PROPERTY_TYPE_STRING
Definition: PropertyTypeConstants.java:237
com.google.appinventor.components.runtime.GameClient.GameClient
GameClient(ComponentContainer container)
Definition: GameClient.java:147
com.google.appinventor.components
com.google.appinventor.components.runtime.GameClient.Initialize
void Initialize()
Definition: GameClient.java:386
com.google.appinventor.components.runtime.util.GameInstance
Definition: GameInstance.java:20
com.google.appinventor.components.runtime.util.JsonUtil
Definition: JsonUtil.java:42
com.google.appinventor.components.annotations.DesignerComponent
Definition: DesignerComponent.java:22
com.google.appinventor.components.annotations.SimpleEvent
Definition: SimpleEvent.java:20
com.google.appinventor.components.annotations.PropertyCategory.BEHAVIOR
BEHAVIOR
Definition: PropertyCategory.java:15
com.google.appinventor.components.runtime.util.GameInstance.putMessageTime
void putMessageTime(String type, String time)
Definition: GameInstance.java:142
com.google.appinventor.components.runtime.util.WebServiceUtil
Definition: WebServiceUtil.java:45
com.google.appinventor.components.runtime.util.GameInstance.setPlayers
PlayerListDelta setPlayers(List< String > newPlayersList)
Definition: GameInstance.java:84
com.google.appinventor.components.runtime.collect
Definition: Lists.java:7
com.google.appinventor.components.runtime.util.GameInstance.getLeader
String getLeader()
Definition: GameInstance.java:61
com.google.appinventor.components.runtime.collect.Lists.newArrayList
static< E > ArrayList< E > newArrayList()
Definition: Lists.java:30
com.google.appinventor.components.annotations.UsesPermissions
Definition: UsesPermissions.java:21
com.google.appinventor.components.runtime.OnResumeListener
Definition: OnResumeListener.java:14
com.google.appinventor.components.runtime.util.GameInstance.getInstanceId
String getInstanceId()
Definition: GameInstance.java:53
com.google.appinventor.components.runtime.EventDispatcher.dispatchEvent
static boolean dispatchEvent(Component component, String eventName, Object...args)
Definition: EventDispatcher.java:188
com.google.appinventor.components.runtime.AndroidNonvisibleComponent
Definition: AndroidNonvisibleComponent.java:17
com.google.appinventor.components.runtime.errors.YailRuntimeError
Definition: YailRuntimeError.java:14
com.google.appinventor.components.annotations.SimpleProperty
Definition: SimpleProperty.java:23
com.google.appinventor.components.runtime.util.AsynchUtil.runAsynchronously
static void runAsynchronously(final Runnable call)
Definition: AsynchUtil.java:23
com.google.appinventor.components.annotations.PropertyCategory
Definition: PropertyCategory.java:13
com.google.appinventor.components.runtime.ComponentContainer
Definition: ComponentContainer.java:16
com.google.appinventor.components.runtime
Copyright 2009-2011 Google, All Rights reserved.
Definition: AccelerometerSensor.java:8
com.google.appinventor.components.runtime.Component
Definition: Component.java:17
com.google.appinventor.components.runtime.collect.Lists
Definition: Lists.java:20
com.google.appinventor.components.runtime.util.YailList.toJSONString
String toJSONString()
Definition: YailList.java:148
com.google.appinventor.components.common
Definition: ComponentCategory.java:7
com.google.appinventor.components.common.ComponentCategory
Definition: ComponentCategory.java:48
com.google.appinventor.components.runtime.OnStopListener
Definition: OnStopListener.java:15
com.google.appinventor.components.runtime.GameClient.onStop
void onStop()
Definition: GameClient.java:1120
com.google.appinventor.components.annotations.SimpleObject
Definition: SimpleObject.java:23
com.google.appinventor.components.runtime.util.AsynchUtil
Definition: AsynchUtil.java:17
com.google
com
com.google.appinventor.components.runtime.errors
Definition: ArrayIndexOutOfBoundsError.java:7
com.google.appinventor.components.runtime.ComponentContainer.$form
Form $form()
com.google.appinventor.components.runtime.util.GameInstance.setLeader
void setLeader(String leader)
Definition: GameInstance.java:69
com.google.appinventor.components.runtime.ComponentContainer.$context
Activity $context()
com.google.appinventor.components.runtime.GameClient.onResume
void onResume()
Definition: GameClient.java:1111
com.google.appinventor.components.runtime.util.GameInstance.getPlayers
List< String > getPlayers()
Definition: GameInstance.java:108
com.google.appinventor.components.common.PropertyTypeConstants
Definition: PropertyTypeConstants.java:14
com.google.appinventor.components.runtime.util.GameInstance.getMessageTime
String getMessageTime(String type)
Definition: GameInstance.java:123
com.google.appinventor.components.annotations
com.google.appinventor.components.runtime.util.AsyncCallbackPair
Definition: AsyncCallbackPair.java:17
com.google.appinventor