Skip to content

Android Sample WalkThrough

DhruvCShepHertz edited this page May 18, 2013 · 6 revisions

We will use the classic Tic Tac Toe Android game developed using AppWarp and App42 SDK together as an example.

Lets browse through the source code and walkthrough some of the key concepts in building this simple game.

First add the following to your application manifest.

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

House Keeping

We write a helper class which wraps around the functionality exposed by the SDK specific to our scenario. This is done in AsyncApp42ServiceApi.java

	private AsyncApp42ServiceApi(){
		// initialize the singletons.
		ServiceAPI sp = new ServiceAPI(Constants.App42ApiKey, Constants.App42ApiSecret);
		WarpClient.initialize(Constants.App42ApiKey, Constants.App42ApiSecret);
		this.userService = sp.buildUserService();    	
	}

	public static WarpClient getMyWarpClient(){
		if(warpClient == null){
		try {
	            warpClient = WarpClient.getInstance();
		} catch (Exception e) {
	            e.printStackTrace();
		}
	    }

	    return warpClient;
	}

	public static AsyncApp42ServiceApi instance()
	{
	    if(mInstance == null)
	    {
	        mInstance = new AsyncApp42ServiceApi();
	    }

	    return mInstance;
	}

MainActivity

In this screen we authenticate the user using App42 cloud service and then once authenticated we connect with AppWarp cloud service.

We wrap the calls to App42 User Service in a helper method which executes the API in a new thread and posts the result back on to the calling thread (UI thread). This prevents the UI thread from blocking while the REST call is happening in the background.

public void authenticateUser(final String name, final String pswd, final App42ServiceListener callBack){
		final Handler callerThreadHandler = new Handler();
        new Thread(){
            @Override public void run() {
                try {                        
            		final App42Response response = userService.authenticate(name, pswd);
            		callerThreadHandler.post(new Runnable() {
                        @Override
                        public void run() {      
                        	callBack.onUserAuthenticated(response);
                        }            			
            		});            		
                }  
            	catch (App42Exception ex) {   
            		callBack.onUserAuthenticated(null);
            	}
            }
        }.start();
}

Similar to authenticate, we write another Async helper method for registerUser.

Once the user is authenticated, we go ahead and connect with AppWarp Cloud Service. In order to receive callback result from the connect operation, we need to add a connection request listener. We make the MainActivity class implement the ConnectionRequestListener and implement the methods.

AsyncApp42ServiceApi.getMyWarpClient().connectWithUserName(userName.getText().toString()); 

If we get a successful callback for this operation, we go ahead and enter the UserHomeActivity.

	@Override
	public void onConnectDone(ConnectEvent event) {
		if(event.getResult() == WarpResponseResultCode.SUCCESS){
			// yay! we are connected to Warp server
			gotoHomeActivity(userName.getText().toString());
		}		
	}

	@Override
	public void onDisconnectDone(ConnectEvent event) {
		// TODO Handle error
	}

UserHomeActivity

This screen shows two options to the user. Either to join a random game or to start a new game. If the user wants to join a random game, we get the list of all the rooms and then query each one till we find a room in which there is one player present. If found, we go ahead and join that room so the game can start. If the user selects to start a new game, we create a room with the name given and the player will join that room and wait till some other player also joins this room.

	public void onJoinRandomGameClicked(View view){
		AsyncApp42ServiceApi.getMyWarpClient().addZoneRequestListener(this);
		AsyncApp42ServiceApi.getMyWarpClient().getAllRooms();
		progressDialog = ProgressDialog.show(this, "", "joining a random game..");
	}

The above click handler will start the process of finding a relevant room. The first part of this is to get the list of all rooms. To receive the callback for this, we need to add a zone request listener as this is a zone level request. We make the UserHomeActivity implement the zone request listener interface and itself as the listener.

	@Override
	public void onGetAllRoomsDone(AllRoomsEvent event) {
		if((event.getResult() == WarpResponseResultCode.SUCCESS) && (event.getRoomIds()!=null)){
			for(int i=0; i<event.getRoomIds().length; i++ ){
				roomsList.add(event.getRoomIds()[i]);
			}

			// start finding first available room to join.	
			if(roomsList.size() > 0){						
				AsyncApp42ServiceApi.getMyWarpClient().getLiveRoomInfo(roomsList.remove(0));				
			}
		}
		else{
			progressDialog.dismiss();
		}

	}

We save the list of the rooms and call getLiveRoomInfo on each of the rooms one by one till we find the desired room. To receive a callback for the getLiveRoomInfo request, we need to add a room request listener. We make the UserHomeActivity implement this interface and add itself as a listener.

	@Override
	public void onGetLiveRoomInfoDone(LiveRoomInfoEvent event) {
		boolean done = false;
		if(event.getResult() == WarpResponseResultCode.SUCCESS){
			String[] users = event.getJoinedUsers();
			if(users!=null && users.length == 1){				
				// yay found a room I can join!
				roomIdToJoin = event.getData().getId();				
				AsyncApp42ServiceApi.getMyWarpClient().joinRoom(roomIdToJoin);
				done = true;
			}
		}		
		if(!done){
			// continue finding first available room to join.
			if(roomsList.size() > 0){			
				AsyncApp42ServiceApi.getMyWarpClient().getLiveRoomInfo(roomsList.remove(0));				
			}			
			else{

				// callback is not the main UI thread. So post message to UI thread
				// through its handler. Android UI elements can't be accessed from
				// non-UI threads.		
				UIThreadHandler.post(new Runnable() {
		            @Override
		            public void run() {    
		            	progressDialog.dismiss();
		            }
		        });			
			}
		}

	}

The other option a user has here to start a new game.

	public void onStartGameClicked(View view) {
		progressDialog = ProgressDialog.show(this, "", "creating new room");
		AsyncApp42ServiceApi.getMyWarpClient().addZoneRequestListener(this);
		AsyncApp42ServiceApi.getMyWarpClient().createRoom(newGameName.getText().toString(), userName, 2);

	}

This will simply create a new room with the name given by the user and specify maxUsers as 2 and the local user as the owner. The result of the request will be invoked on our zone request listener interface.

	@Override
	public void onCreateRoomDone(RoomEvent arg0) {
		if(arg0.getResult() == WarpResponseResultCode.SUCCESS){
			roomIdToJoin = arg0.getData().getId();
			AsyncApp42ServiceApi.getMyWarpClient().joinRoom(roomIdToJoin);
		}		
	}

Once a room is created, we can go ahead and join it as in the previous option of joining a random room. Either way once the room has been joined we are ready to move forward to the game play screen.

	@Override
	public void onJoinRoomDone(RoomEvent event) {

		if(event.getResult() == WarpResponseResultCode.SUCCESS){
			// callback is not the main UI thread. So post message to UI thread
			// through its handler. Android UI elements can't be accessed from
			// non-UI threads.		
			UIThreadHandler.post(new Runnable() {
	            @Override
	            public void run() {    
	            	progressDialog.dismiss();
	    			Intent myIntent = new Intent(UserHomeActivity.this, GameActivity.class);
	    			myIntent.putExtra(Constants.IntentGameRoomId, roomIdToJoin);
	    			myIntent.putExtra(Constants.IntentUserName, userName);
	    			UserHomeActivity.this.startActivity(myIntent);
	            }
	        });
		}		
	}

Game Activity

In this screen we utilize the customRoomData and updatePeers api. We save the game board state as customRoomData and use updatePeers to send a trigger when a player whose turn it is updates the board.

	@Override
	public void onCreate(Bundle savedInstanceState) { 
		super.onCreate(savedInstanceState);
		setContentView(R.layout.game);						
                Intent intent = getIntent();
                localUserName = intent.getStringExtra(Constants.IntentUserName);
		gameRoomId = intent.getStringExtra(Constants.IntentGameRoomId);
		AsyncApp42ServiceApi.getMyWarpClient().addRoomRequestListener(this);
		AsyncApp42ServiceApi.getMyWarpClient().addNotificationListener(this);	
		AsyncApp42ServiceApi.getMyWarpClient().subscribeRoom(gameRoomId);
	}

In the onCreate method we register the activity as a room request listener and notification listener to receive the updatePeer triggers. We also need to subscribe the room in order for the server to send notifications to us from this room.

The customRoomData set describes the current state of the game board. We construct this JSON once and then once set keep updating as the game goes on. Here is a Util method which constructs the JSON.

	public static JSONObject buildNewGameJSON(String username){
		// prepare the gameObj myself as I'm the one who joined first.
		JSONObject gameObject = new JSONObject();

		try {
			gameObject.put(Constants.GameFirstUserKey, username);
			gameObject.put(Constants.GameSecondUserKey, "");
			gameObject.put(Constants.GameStateKey, Constants.GameStateIdle);					
			gameObject.put(Constants.GameBoardKey, Constants.GameEmptyBoard);			
			gameObject.put(Constants.GameWinnerKey, "");
			gameObject.put(Constants.GameNextMoveKey, username);
		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return gameObject;		
	}

Once the user makes his selection, we update the board and use setCustomRoomData and then send an updatePeers Trigger to inform the other player that there is has been an update.

	public void onSubmitClicked(View view) {
		try {
			String boardState = gameObject.getString(Constants.GameBoardKey);
			/*
			make the changes to the board state string and update gameObject json
			*/
			AsyncApp42ServiceApi.getMyWarpClient().setCustomRoomData(gameRoomId, gameObject.toString());
			AsyncApp42ServiceApi.getMyWarpClient().sendUpdatePeers(Constants.UpdateTrigger);

		} catch (JSONException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

Then we get an update, we simply need to refresh the UI by getting the current customRoomData which has been updated by the remote player

	@Override
	public void onUpdatePeersReceived(UpdateEvent event) {
		AsyncApp42ServiceApi.getMyWarpClient().getLiveRoomInfo(gameRoomId);		
	}

View Source Code