Java tutorial
/* * Copyright (C) 2013 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.cast.samples.tictactoe; import com.google.android.gms.cast.Cast; import com.google.android.gms.cast.CastDevice; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.Status; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.util.Log; /** * An abstract class which encapsulates control and game logic for sending and receiving messages * during a TicTacToe game. */ public abstract class GameChannel implements Cast.MessageReceivedCallback { private static final String TAG = GameChannel.class.getSimpleName(); private static final String GAME_NAMESPACE = "urn:x-cast:com.google.cast.demo.tictactoe"; public static final String END_STATE_X_WON = "X-won"; public static final String END_STATE_O_WON = "O-won"; public static final String END_STATE_DRAW = "draw"; public static final String END_STATE_ABANDONED = "abandoned"; public static final String PLAYER_X = "X"; public static final String PLAYER_O = "O"; // Receivable event types private static final String KEY_BOARD_LAYOUT_RESPONSE = "board_layout_response"; private static final String KEY_EVENT = "event"; private static final String KEY_JOINED = "joined"; private static final String KEY_MOVED = "moved"; private static final String KEY_ENDGAME = "endgame"; private static final String KEY_ERROR = "error"; // Commands private static final String KEY_BOARD_LAYOUT_REQUEST = "board_layout_request"; private static final String KEY_COMMAND = "command"; private static final String KEY_JOIN = "join"; private static final String KEY_MOVE = "move"; private static final String KEY_LEAVE = "leave"; private static final String KEY_BOARD = "board"; private static final String KEY_COLUMN = "column"; private static final String KEY_END_STATE = "end_state"; private static final String KEY_GAME_OVER = "game_over"; private static final String KEY_MESSAGE = "message"; private static final String KEY_NAME = "name"; private static final String KEY_OPPONENT = "opponent"; private static final String KEY_PLAYER = "player"; private static final String KEY_ROW = "row"; private static final String KEY_WINNING_LOCATION = "winning_location"; /** * An enum representing board rows, columns, and diagonals as numerical values. */ public enum WinningLocation { ROW_0(0), ROW_1(1), ROW_2(2), COL_0(3), COL_1(4), COL_2(5), DIAGONAL_TOPLEFT(6), DIAGONAL_BOTTOMLEFT( 7), UNKNOWN(-1); int mValue; private WinningLocation(int value) { mValue = value; } public int getValue() { return mValue; } /** * Returns a WinningLocation, given an int value. */ public static WinningLocation fromIntValue(int value) { if (ROW_0.getValue() == value) { return ROW_0; } else if (ROW_1.getValue() == value) { return ROW_1; } else if (ROW_2.getValue() == value) { return ROW_2; } else if (COL_0.getValue() == value) { return COL_0; } else if (COL_1.getValue() == value) { return COL_1; } else if (COL_2.getValue() == value) { return COL_2; } else if (DIAGONAL_TOPLEFT.getValue() == value) { return DIAGONAL_TOPLEFT; } else if (DIAGONAL_BOTTOMLEFT.getValue() == value) { return DIAGONAL_BOTTOMLEFT; } else { return UNKNOWN; } } } /** * Constructs a new GameChannel m with GAME_NAMESPACE as the namespace used by * the superclass. */ protected GameChannel() { } /** * Performs some action upon a player joining the game. * * @param playerSymbol either X or O * @param opponentName the name of the player who just joined an existing game, or the opponent */ protected abstract void onGameJoined(String playerSymbol, String opponentName); /** * Performs some action, or updates the game display upon a move. * * @param playerSymbol either X or O * @param row the row index of the move * @param column the column index of the move * @param isGameOver whether or not the game ended as a result of the move */ protected abstract void onGameMove(String playerSymbol, int row, int column, boolean isGameOver); /** * Performs some action upon game end, depending on game's end state and the position of the * winning pieces. * * @param endState likely to be END_STATE_X_WON, END_STATE_O_WON, or END_STATE_ABANDONED * @param location an int value corresponding to the enum WinningLocation's values */ protected abstract void onGameEnd(String endState, int location); /** * Performs some action upon an int[][] board layout being sent. * * @param boardLayout a 2-D array of ints, likely to be 3x3 */ protected abstract void onGameBoardLayout(int[][] boardLayout); /** * Performs some action upon a game error. * * @param errorMessage the string description of the error */ protected abstract void onGameError(String errorMessage); /** * Returns the namespace for this cast channel. */ public String getNamespace() { return GAME_NAMESPACE; } /** * Attempts to connect to an existing session of the game by sending a join command. * * @param name the name of the player that is joining */ public final void join(GoogleApiClient apiClient, String name) { try { Log.d(TAG, "join: " + name); JSONObject payload = new JSONObject(); payload.put(KEY_COMMAND, KEY_JOIN); payload.put(KEY_NAME, name); sendMessage(apiClient, payload.toString()); } catch (JSONException e) { Log.e(TAG, "Cannot create object to join a game", e); } } /** * Attempts to make a move by sending a command to place a piece in the given row and column. */ public final void move(GoogleApiClient apiClient, final int row, final int column) { Log.d(TAG, "move: row:" + row + " column:" + column); try { JSONObject payload = new JSONObject(); payload.put(KEY_COMMAND, KEY_MOVE); payload.put(KEY_ROW, row); payload.put(KEY_COLUMN, column); sendMessage(apiClient, payload.toString()); } catch (JSONException e) { Log.e(TAG, "Cannot create object to send a move", e); } } /** * Sends a command to leave the current game. */ public final void leave(GoogleApiClient apiClient) { try { Log.d(TAG, "leave"); JSONObject payload = new JSONObject(); payload.put(KEY_COMMAND, KEY_LEAVE); sendMessage(apiClient, payload.toString()); } catch (JSONException e) { Log.e(TAG, "Cannot create object to leave a game", e); } } /** * Sends a command requesting the current layout of the board. */ public final void requestBoardLayout(GoogleApiClient apiClient) { try { Log.d(TAG, "requestBoardLayout"); JSONObject payload = new JSONObject(); payload.put(KEY_COMMAND, KEY_BOARD_LAYOUT_REQUEST); sendMessage(apiClient, payload.toString()); } catch (JSONException e) { Log.e(TAG, "Cannot create object to request board layout", e); } } /** * Processes all Text messages received from the receiver device and performs the appropriate * action for the message. Recognizable messages are of the form: * * <ul> * <li> KEY_JOINED: a player joined the current game * <li> KEY_MOVED: a player made a move * <li> KEY_ENDGAME: the game has ended in one of the END_STATE_* states * <li> KEY_ERROR: a game error has occurred * <li> KEY_BOARD_LAYOUT_RESPONSE: the board has been laid out in some new configuration * </ul> * * <p>No other messages are recognized. */ @Override public void onMessageReceived(CastDevice castDevice, String namespace, String message) { try { Log.d(TAG, "onTextMessageReceived: " + message); JSONObject payload = new JSONObject(message); Log.d(TAG, "payload: " + payload); if (payload.has(KEY_EVENT)) { String event = payload.getString(KEY_EVENT); if (KEY_JOINED.equals(event)) { Log.d(TAG, "JOINED"); try { String player = payload.getString(KEY_PLAYER); String opponentName = payload.getString(KEY_OPPONENT); onGameJoined(player, opponentName); } catch (JSONException e) { e.printStackTrace(); } } else if (KEY_MOVED.equals(event)) { Log.d(TAG, "MOVED"); try { String player = payload.getString(KEY_PLAYER); int row = payload.getInt(KEY_ROW); int column = payload.getInt(KEY_COLUMN); boolean isGameOver = payload.getBoolean(KEY_GAME_OVER); onGameMove(player, row, column, isGameOver); } catch (JSONException e) { e.printStackTrace(); } } else if (KEY_ENDGAME.equals(event)) { Log.d(TAG, "ENDGAME"); try { String endState = payload.getString(KEY_END_STATE); int winningLocation = -1; if (END_STATE_ABANDONED.equals(endState) == false) { winningLocation = payload.getInt(KEY_WINNING_LOCATION); } onGameEnd(endState, winningLocation); } catch (JSONException e) { e.printStackTrace(); } } else if (KEY_ERROR.equals(event)) { Log.d(TAG, "ERROR"); try { String errorMessage = payload.getString(KEY_MESSAGE); onGameError(errorMessage); } catch (JSONException e) { e.printStackTrace(); } } else if (KEY_BOARD_LAYOUT_RESPONSE.equals(event)) { Log.d(TAG, "Board Layout"); int[][] boardLayout = new int[3][3]; try { JSONArray boardJSONArray = payload.getJSONArray(KEY_BOARD); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { boardLayout[i][j] = boardJSONArray.getInt(i * 3 + j); } } onGameBoardLayout(boardLayout); } catch (JSONException e) { e.printStackTrace(); } } } else { Log.w(TAG, "Unknown payload: " + payload); } } catch (JSONException e) { Log.w(TAG, "Message doesn't contain an expected key.", e); } } private final void sendMessage(GoogleApiClient apiClient, String message) { Log.d(TAG, "Sending message: (ns=" + GAME_NAMESPACE + ") " + message); Cast.CastApi.sendMessage(apiClient, GAME_NAMESPACE, message) .setResultCallback(new SendMessageResultCallback(message)); } private final class SendMessageResultCallback implements ResultCallback<Status> { String mMessage; SendMessageResultCallback(String message) { mMessage = message; } @Override public void onResult(Status result) { if (!result.isSuccess()) { Log.d(TAG, "Failed to send message. statusCode: " + result.getStatusCode() + " message: " + mMessage); } } } }