diff --git a/samples/TicTacToeLib/AndroidManifest.xml b/samples/TicTacToeLib/AndroidManifest.xml
index cc667ee2e..9772b88c5 100755
--- a/samples/TicTacToeLib/AndroidManifest.xml
+++ b/samples/TicTacToeLib/AndroidManifest.xml
@@ -19,7 +19,4 @@
package="com.example.tictactoe.library"
android:versionCode="1"
android:versionName="1.0">
-
-
-
\ No newline at end of file
diff --git a/samples/TicTacToeLib/README.txt b/samples/TicTacToeLib/README.txt
new file mode 100755
index 000000000..a43b2f156
--- /dev/null
+++ b/samples/TicTacToeLib/README.txt
@@ -0,0 +1,7 @@
+Sample: TicTacToeLib and TicTacToeMain.
+
+These two projects work together. They demonstrate how to use the ability to
+split an APK into multiple projects.
+
+Please see the README.txt file in ../TicTacToeMain for more details.
+
diff --git a/samples/TicTacToeLib/default.properties b/samples/TicTacToeLib/default.properties
index 4ac577ca4..89927d72e 100755
--- a/samples/TicTacToeLib/default.properties
+++ b/samples/TicTacToeLib/default.properties
@@ -16,13 +16,13 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
+#
# This file must be checked in Version Control Systems.
-#
+#
# To customize properties used by the Ant build system use,
# "build.properties", and override values to adapt the script to your
# project structure.
android.library=true
# Project target.
-target=android-7
+target=android-3
diff --git a/samples/TicTacToeLib/res/layout-land/lib_game.xml b/samples/TicTacToeLib/res/layout-land/lib_game.xml
new file mode 100755
index 000000000..c0f5cbab2
--- /dev/null
+++ b/samples/TicTacToeLib/res/layout-land/lib_game.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/TicTacToeLib/res/layout/lib_game.xml b/samples/TicTacToeLib/res/layout/lib_game.xml
index c4e674de9..75c284a68 100755
--- a/samples/TicTacToeLib/res/layout/lib_game.xml
+++ b/samples/TicTacToeLib/res/layout/lib_game.xml
@@ -19,13 +19,13 @@
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:weightSum="2"
+ android:gravity="center_horizontal"
>
@@ -35,16 +35,16 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
- android:layout_weight="1"
+ android:layout_marginBottom="10dip"
/>
diff --git a/samples/TicTacToeLib/res/values/strings.xml b/samples/TicTacToeLib/res/values/strings.xml
index 5707400cb..9fd841358 100755
--- a/samples/TicTacToeLib/res/values/strings.xml
+++ b/samples/TicTacToeLib/res/values/strings.xml
@@ -16,6 +16,5 @@
-->
-
-
+
diff --git a/samples/TicTacToeLib/src/com/example/tictactoe/library/GameActivity.java b/samples/TicTacToeLib/src/com/example/tictactoe/library/GameActivity.java
index f75665e6c..e1514cdc2 100755
--- a/samples/TicTacToeLib/src/com/example/tictactoe/library/GameActivity.java
+++ b/samples/TicTacToeLib/src/com/example/tictactoe/library/GameActivity.java
@@ -16,15 +16,36 @@
package com.example.tictactoe.library;
+import java.util.Random;
+
import android.app.Activity;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Handler.Callback;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.example.tictactoe.library.GameView.ICellListener;
+import com.example.tictactoe.library.GameView.State;
+
public class GameActivity extends Activity {
- public final static String EXTRA_START_WITH_HUMAN =
- "com.example.tictactoe.library.GameActivity.EXTRA_START_WITH_HUMAN";
+ /** Start player. Must be 1 or 2. Default is 1. */
+ public static final String EXTRA_START_PLAYER =
+ "com.example.tictactoe.library.GameActivity.EXTRA_START_PLAYER";
- private boolean mTurnIsHuman;
+ private static final int MSG_COMPUTER_TURN = 1;
+ private static final long COMPUTER_DELAY_MS = 500;
+
+ private Handler mHandler = new Handler(new MyHandlerCallback());
+ private Random mRnd = new Random();
+ private GameView mGameView;
+ private TextView mInfoView;
+ private Button mButtonNext;
/** Called when the activity is first created. */
@Override
@@ -32,22 +53,207 @@ public class GameActivity extends Activity {
super.onCreate(bundle);
/*
- * IMPORTANT TIP: all resource IDs from this library must be
- * different from the projects that will include it. E.g.
- * if my layout were named "main.xml", I would have to use the ID
- * R.layout.main; however there is already a *different*
- * ID with the same name in the main project and when the library
- * gets merged in the project the wrong ID would end up being used.
+ * IMPORTANT: all resource IDs from this library will eventually be merged
+ * with the resources from the main project that will use the library.
*
- * To avoid such potential conflicts, it's probably a good idea
- * to add a prefix to the library resource names.
+ * If the main project and the libraries define the same resource IDs,
+ * the application project will always have priority and override library resources
+ * and IDs defined in multiple libraries are resolved based on the libraries priority
+ * defined in the main project.
+ *
+ * An intentional consequence is that the main project can override some resources
+ * from the library.
+ * (TODO insert example).
+ *
+ * To avoid potential conflicts, it is suggested to add a prefix to the
+ * library resource names.
*/
setContentView(R.layout.lib_game);
- mTurnIsHuman = getIntent().getBooleanExtra(
- EXTRA_START_WITH_HUMAN, true);
+ mGameView = (GameView) findViewById(R.id.game_view);
+ mInfoView = (TextView) findViewById(R.id.info_turn);
+ mButtonNext = (Button) findViewById(R.id.next_turn);
+ mGameView.setFocusable(true);
+ mGameView.setFocusableInTouchMode(true);
+ mGameView.setCellListener(new MyCellListener());
+ mButtonNext.setOnClickListener(new MyButtonListener());
}
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ State player = mGameView.getCurrentPlayer();
+ if (player == State.UNKNOWN) {
+ player = State.fromInt(getIntent().getIntExtra(EXTRA_START_PLAYER, 1));
+ if (!checkGameFinished(player)) {
+ selectTurn(player);
+ }
+ }
+ if (player == State.PLAYER2) {
+ mHandler.sendEmptyMessageDelayed(MSG_COMPUTER_TURN, COMPUTER_DELAY_MS);
+ }
+ if (player == State.WIN) {
+ setWinState(mGameView.getWinner());
+ }
+ }
+
+
+ private State selectTurn(State player) {
+ mGameView.setCurrentPlayer(player);
+ mButtonNext.setEnabled(false);
+
+ if (player == State.PLAYER1) {
+ mInfoView.setText("Player 1's turn -- that's you!");
+ mGameView.setEnabled(true);
+
+ } else if (player == State.PLAYER2) {
+ mInfoView.setText("Player 2's turn (that's the computer)");
+ mGameView.setEnabled(false);
+ }
+
+ return player;
+ }
+
+ private class MyCellListener implements ICellListener {
+ public void onCellSelected() {
+ if (mGameView.getCurrentPlayer() == State.PLAYER1) {
+ int cell = mGameView.getSelection();
+ mButtonNext.setEnabled(cell >= 0);
+ }
+ }
+ }
+
+ private class MyButtonListener implements OnClickListener {
+
+ public void onClick(View v) {
+ State player = mGameView.getCurrentPlayer();
+
+ if (player == State.WIN) {
+ GameActivity.this.finish();
+
+ } else if (player == State.PLAYER1) {
+ int cell = mGameView.getSelection();
+ if (cell >= 0) {
+ mGameView.stopBlink();
+ mGameView.setCell(cell, player);
+ finishTurn();
+ }
+ }
+ }
+ }
+
+ private class MyHandlerCallback implements Callback {
+ public boolean handleMessage(Message msg) {
+ if (msg.what == MSG_COMPUTER_TURN) {
+
+ // Pick a non-used cell at random. That's about all the AI you need for this game.
+ State[] data = mGameView.getData();
+ int used = 0;
+ while (used != 0x1F) {
+ int index = mRnd.nextInt(9);
+ if (((used >> index) & 1) == 0) {
+ used |= 1 << index;
+ if (data[index] == State.EMPTY) {
+ mGameView.setCell(index, mGameView.getCurrentPlayer());
+ break;
+ }
+ }
+ }
+
+ finishTurn();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private State getOtherPlayer(State player) {
+ return player == State.PLAYER1 ? State.PLAYER2 : State.PLAYER1;
+ }
+
+ private void finishTurn() {
+ State player = mGameView.getCurrentPlayer();
+ if (!checkGameFinished(player)) {
+ player = selectTurn(getOtherPlayer(player));
+ if (player == State.PLAYER2) {
+ mHandler.sendEmptyMessageDelayed(MSG_COMPUTER_TURN, COMPUTER_DELAY_MS);
+ }
+ }
+ }
+
+ public boolean checkGameFinished(State player) {
+ State[] data = mGameView.getData();
+ boolean full = true;
+
+ int col = -1;
+ int row = -1;
+ int diag = -1;
+
+ // check rows
+ for (int j = 0, k = 0; j < 3; j++, k += 3) {
+ if (data[k] != State.EMPTY && data[k] == data[k+1] && data[k] == data[k+2]) {
+ row = j;
+ }
+ if (full && (data[k] == State.EMPTY ||
+ data[k+1] == State.EMPTY ||
+ data[k+2] == State.EMPTY)) {
+ full = false;
+ }
+ }
+
+ // check columns
+ for (int i = 0; i < 3; i++) {
+ if (data[i] != State.EMPTY && data[i] == data[i+3] && data[i] == data[i+6]) {
+ col = i;
+ }
+ }
+
+ // check diagonals
+ if (data[0] != State.EMPTY && data[0] == data[1+3] && data[0] == data[2+6]) {
+ diag = 0;
+ } else if (data[2] != State.EMPTY && data[2] == data[1+3] && data[2] == data[0+6]) {
+ diag = 1;
+ }
+
+ if (col != -1 || row != -1 || diag != -1) {
+ setFinished(player, col, row, diag);
+ return true;
+ }
+
+ // if we get here, there's no winner but the board is full.
+ if (full) {
+ setFinished(State.EMPTY, -1, -1, -1);
+ return true;
+ }
+ return false;
+ }
+
+ private void setFinished(State player, int col, int row, int diagonal) {
+
+ mGameView.setCurrentPlayer(State.WIN);
+ mGameView.setWinner(player);
+ mGameView.setEnabled(false);
+ mGameView.setFinished(col, row, diagonal);
+
+ setWinState(player);
+ }
+
+ private void setWinState(State player) {
+ mButtonNext.setEnabled(true);
+ mButtonNext.setText("Back");
+
+ String text;
+
+ if (player == State.EMPTY) {
+ text = "This is a tie! No one wins!";
+ } else if (player == State.PLAYER1) {
+ text = "Player 1 (you) wins!";
+ } else {
+ text = "Player 2 (computer) wins!";
+ }
+ mInfoView.setText(text);
+ }
}
diff --git a/samples/TicTacToeLib/src/com/example/tictactoe/library/GameView.java b/samples/TicTacToeLib/src/com/example/tictactoe/library/GameView.java
index 2c27b503e..89f858444 100755
--- a/samples/TicTacToeLib/src/com/example/tictactoe/library/GameView.java
+++ b/samples/TicTacToeLib/src/com/example/tictactoe/library/GameView.java
@@ -16,7 +16,7 @@
package com.example.tictactoe.library;
-import java.lang.reflect.Field;
+import java.util.Random;
import android.content.Context;
import android.content.res.Resources;
@@ -25,10 +25,14 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory.Options;
+import android.graphics.Paint.Style;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.Parcelable;
import android.os.Handler.Callback;
import android.util.AttributeSet;
import android.util.Log;
@@ -39,41 +43,166 @@ import android.view.View;
public class GameView extends View {
- private final static String TAG = "GameView";
-
- private static final int MSG_FRAME = 1;
+ private static final String TAG = "GameView";
public static final long FPS_MS = 1000/2;
- public static final int EMPTY = 0;
- public static final int CROSS = 1;
- public static final int CIRCLE = 2;
+ public enum State {
+ UNKNOWN(-3),
+ WIN(-2),
+ EMPTY(0),
+ PLAYER1(1),
+ PLAYER2(2);
+ private int mValue;
- /** Contains one of {@link #EMPTY}, {@link #CROSS} or {@link #CIRCLE} */
- private final int[] mData = new int[9];
+ private State(int value) {
+ mValue = value;
+ }
- private final Rect mBgRect = new Rect();
- private final Rect mTempDst = new Rect();
+ public int getValue() {
+ return mValue;
+ }
+
+ public static State fromInt(int i) {
+ for (State s : values()) {
+ if (s.getValue() == i) {
+ return s;
+ }
+ }
+ return EMPTY;
+ }
+ }
+
+ private static final int MARGIN = 4;
+ private static final int MSG_BLINK = 1;
+
+ private final Handler mHandler = new Handler(new MyHandler());
+
+ private final Rect mSrcRect = new Rect();
+ private final Rect mDstRect = new Rect();
private int mSxy;
private int mOffetX;
private int mOffetY;
+ private Paint mWinPaint;
private Paint mLinePaint;
+ private Paint mBmpPaint;
+ private Bitmap mBmpPlayer1;
+ private Bitmap mBmpPlayer2;
private Drawable mDrawableBg;
+ private ICellListener mCellListener;
+
+ /** Contains one of {@link State#EMPTY}, {@link State#PLAYER1} or {@link State#PLAYER2}. */
+ private final State[] mData = new State[9];
+
+ private int mSelectedCell = -1;
+ private State mSelectedValue = State.EMPTY;
+ private State mCurrentPlayer = State.UNKNOWN;
+ private State mWinner = State.EMPTY;
+
+ private int mWinCol = -1;
+ private int mWinRow = -1;
+ private int mWinDiag = -1;
+
+ private boolean mBlinkDisplayOff;
+ private final Rect mBlinkRect = new Rect();
+
+
+
+ public interface ICellListener {
+ abstract void onCellSelected();
+ }
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
requestFocus();
mDrawableBg = getResources().getDrawable(R.drawable.lib_bg);
+ setBackgroundDrawable(mDrawableBg);
+
+ mBmpPlayer1 = getResBitmap(R.drawable.lib_cross);
+ mBmpPlayer2 = getResBitmap(R.drawable.lib_circle);
+
+ if (mBmpPlayer1 != null) {
+ mSrcRect.set(0, 0, mBmpPlayer1.getWidth() -1, mBmpPlayer1.getHeight() - 1);
+ }
+
+ mBmpPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mLinePaint = new Paint();
mLinePaint.setColor(0xFFFFFFFF);
mLinePaint.setStrokeWidth(5);
+ mLinePaint.setStyle(Style.STROKE);
+
+ mWinPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mWinPaint.setColor(0xFFFF0000);
+ mWinPaint.setStrokeWidth(10);
+ mWinPaint.setStyle(Style.STROKE);
+
+ for (int i = 0; i < mData.length; i++) {
+ mData[i] = State.EMPTY;
+ }
+
+ if (isInEditMode()) {
+ // In edit mode (e.g. in the Eclipse ADT graphical layout editor)
+ // we'll use some random data to display the state.
+ Random rnd = new Random();
+ for (int i = 0; i < mData.length; i++) {
+ mData[i] = State.fromInt(rnd.nextInt(3));
+ }
+ }
}
+ public State[] getData() {
+ return mData;
+ }
+
+ public void setCell(int cellIndex, State value) {
+ mData[cellIndex] = value;
+ invalidate();
+ }
+
+ public void setCellListener(ICellListener cellListener) {
+ mCellListener = cellListener;
+ }
+
+ public int getSelection() {
+ if (mSelectedValue == mCurrentPlayer) {
+ return mSelectedCell;
+ }
+
+ return -1;
+ }
+
+ public State getCurrentPlayer() {
+ return mCurrentPlayer;
+ }
+
+ public void setCurrentPlayer(State player) {
+ mCurrentPlayer = player;
+ mSelectedCell = -1;
+ }
+
+ public State getWinner() {
+ return mWinner;
+ }
+
+ public void setWinner(State winner) {
+ mWinner = winner;
+ }
+
+ /** Sets winning mark on specified column or row (0..2) or diagonal (0..1). */
+ public void setFinished(int col, int row, int diagonal) {
+ mWinCol = col;
+ mWinRow = row;
+ mWinDiag = diagonal;
+ }
+
+ //-----------------------------------------
+
+
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -83,24 +212,69 @@ public class GameView extends View {
int x7 = mOffetX;
int y7 = mOffetY;
- mDrawableBg.draw(canvas);
-
for (int i = 0, k = sxy; i < 2; i++, k += sxy) {
- canvas.drawLine(x7 , y7 + k, x7 + s3, y7 + k , mLinePaint);
- canvas.drawLine(x7 + k, y7 , x7 + k , y7 + s3, mLinePaint);
+ canvas.drawLine(x7 , y7 + k, x7 + s3 - 1, y7 + k , mLinePaint);
+ canvas.drawLine(x7 + k, y7 , x7 + k , y7 + s3 - 1, mLinePaint);
}
for (int j = 0, k = 0, y = y7; j < 3; j++, y += sxy) {
- for (int i = 0, x = x7; i < 3; i++, x += sxy) {
+ for (int i = 0, x = x7; i < 3; i++, k++, x += sxy) {
+ mDstRect.offsetTo(MARGIN+x, MARGIN+y);
+ State v;
+ if (mSelectedCell == k) {
+ if (mBlinkDisplayOff) {
+ continue;
+ }
+ v = mSelectedValue;
+ } else {
+ v = mData[k];
+ }
+
+ switch(v) {
+ case PLAYER1:
+ if (mBmpPlayer1 != null) {
+ canvas.drawBitmap(mBmpPlayer1, mSrcRect, mDstRect, mBmpPaint);
+ }
+ break;
+ case PLAYER2:
+ if (mBmpPlayer2 != null) {
+ canvas.drawBitmap(mBmpPlayer2, mSrcRect, mDstRect, mBmpPaint);
+ }
+ break;
+ }
}
}
+
+ if (mWinRow >= 0) {
+ int y = y7 + mWinRow * sxy + sxy / 2;
+ canvas.drawLine(x7 + MARGIN, y, x7 + s3 - 1 - MARGIN, y, mWinPaint);
+
+ } else if (mWinCol >= 0) {
+ int x = x7 + mWinCol * sxy + sxy / 2;
+ canvas.drawLine(x, y7 + MARGIN, x, y7 + s3 - 1 - MARGIN, mWinPaint);
+
+ } else if (mWinDiag == 0) {
+ // diagonal 0 is from (0,0) to (2,2)
+
+ canvas.drawLine(x7 + MARGIN, y7 + MARGIN,
+ x7 + s3 - 1 - MARGIN, y7 + s3 - 1 - MARGIN, mWinPaint);
+
+ } else if (mWinDiag == 1) {
+ // diagonal 1 is from (0,2) to (2,0)
+
+ canvas.drawLine(x7 + MARGIN, y7 + s3 - 1 - MARGIN,
+ x7 + s3 - 1 - MARGIN, y7 + MARGIN, mWinPaint);
+ }
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ // Keep the view squared
+ int w = MeasureSpec.getSize(widthMeasureSpec);
+ int h = MeasureSpec.getSize(heightMeasureSpec);
+ int d = w == 0 ? h : h == 0 ? w : w < h ? w : h;
+ setMeasuredDimension(d, d);
}
@Override
@@ -108,8 +282,8 @@ public class GameView extends View {
super.onSizeChanged(w, h, oldw, oldh);
Log.d(TAG, String.format("onSizeChanged: %dx%d", w, h));
- int sx = w / 3;
- int sy = h / 3;
+ int sx = (w - 2 * MARGIN) / 3;
+ int sy = (h - 2 * MARGIN) / 3;
int size = sx < sy ? sx : sy;
@@ -117,8 +291,7 @@ public class GameView extends View {
mOffetX = (w - 3 * size) / 2;
mOffetY = (h - 3 * size) / 2;
- mDrawableBg.setBounds(mOffetX, mOffetY,
- mOffetX + 3 * size, mOffetY + 3 * size);
+ mDstRect.set(MARGIN, MARGIN, size - MARGIN, size - MARGIN);
}
@Override
@@ -128,23 +301,170 @@ public class GameView extends View {
if (action == MotionEvent.ACTION_DOWN) {
return true;
- } else if (action == MotionEvent.ACTION_DOWN) {
- float x = event.getX();
- float y = event.getY();
+ } else if (action == MotionEvent.ACTION_UP) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+
+ int sxy = mSxy;
+ x = (x - MARGIN) / sxy;
+ y = (y - MARGIN) / sxy;
+
+ if (isEnabled() && x >= 0 && x < 3 && y >= 0 & y < 3) {
+ int cell = x + 3 * y;
+
+ State state = cell == mSelectedCell ? mSelectedValue : mData[cell];
+ state = state == State.EMPTY ? mCurrentPlayer : State.EMPTY;
+
+ stopBlink();
+
+ mSelectedCell = cell;
+ mSelectedValue = state;
+ mBlinkDisplayOff = false;
+ mBlinkRect.set(MARGIN + x * sxy, MARGIN + y * sxy,
+ MARGIN + (x + 1) * sxy, MARGIN + (y + 1) * sxy);
+
+ if (state != State.EMPTY) {
+ // Start the blinker
+ mHandler.sendEmptyMessageDelayed(MSG_BLINK, FPS_MS);
+ }
+
+ if (mCellListener != null) {
+ mCellListener.onCellSelected();
+ }
+ }
- // TODO
return true;
}
return false;
}
- public Bitmap getResBitmap(int bmpResId) {
+ public void stopBlink() {
+ boolean hadSelection = mSelectedCell != -1 && mSelectedValue != State.EMPTY;
+ mSelectedCell = -1;
+ mSelectedValue = State.EMPTY;
+ if (!mBlinkRect.isEmpty()) {
+ invalidate(mBlinkRect);
+ }
+ mBlinkDisplayOff = false;
+ mBlinkRect.setEmpty();
+ mHandler.removeMessages(MSG_BLINK);
+ if (hadSelection && mCellListener != null) {
+ mCellListener.onCellSelected();
+ }
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Bundle b = new Bundle();
+
+ Parcelable s = super.onSaveInstanceState();
+ b.putParcelable("gv_super_state", s);
+
+ b.putBoolean("gv_en", isEnabled());
+
+ int[] data = new int[mData.length];
+ for (int i = 0; i < data.length; i++) {
+ data[i] = mData[i].getValue();
+ }
+ b.putIntArray("gv_data", data);
+
+ b.putInt("gv_sel_cell", mSelectedCell);
+ b.putInt("gv_sel_val", mSelectedValue.getValue());
+ b.putInt("gv_curr_play", mCurrentPlayer.getValue());
+ b.putInt("gv_winner", mWinner.getValue());
+
+ b.putInt("gv_win_col", mWinCol);
+ b.putInt("gv_win_row", mWinRow);
+ b.putInt("gv_win_diag", mWinDiag);
+
+ b.putBoolean("gv_blink_off", mBlinkDisplayOff);
+ b.putParcelable("gv_blink_rect", mBlinkRect);
+
+ return b;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+
+ if (!(state instanceof Bundle)) {
+ // Not supposed to happen.
+ super.onRestoreInstanceState(state);
+ return;
+ }
+
+ Bundle b = (Bundle) state;
+ Parcelable superState = b.getParcelable("gv_super_state");
+
+ setEnabled(b.getBoolean("gv_en", true));
+
+ int[] data = b.getIntArray("gv_data");
+ if (data != null && data.length == mData.length) {
+ for (int i = 0; i < data.length; i++) {
+ mData[i] = State.fromInt(data[i]);
+ }
+ }
+
+ mSelectedCell = b.getInt("gv_sel_cell", -1);
+ mSelectedValue = State.fromInt(b.getInt("gv_sel_val", State.EMPTY.getValue()));
+ mCurrentPlayer = State.fromInt(b.getInt("gv_curr_play", State.EMPTY.getValue()));
+ mWinner = State.fromInt(b.getInt("gv_winner", State.EMPTY.getValue()));
+
+ mWinCol = b.getInt("gv_win_col", -1);
+ mWinRow = b.getInt("gv_win_row", -1);
+ mWinDiag = b.getInt("gv_win_diag", -1);
+
+ mBlinkDisplayOff = b.getBoolean("gv_blink_off", false);
+ Rect r = b.getParcelable("gv_blink_rect");
+ if (r != null) {
+ mBlinkRect.set(r);
+ }
+
+ // let the blink handler decide if it should blink or not
+ mHandler.sendEmptyMessage(MSG_BLINK);
+
+ super.onRestoreInstanceState(superState);
+ }
+
+ //-----
+
+ private class MyHandler implements Callback {
+ public boolean handleMessage(Message msg) {
+ if (msg.what == MSG_BLINK) {
+ if (mSelectedCell >= 0 && mSelectedValue != State.EMPTY && mBlinkRect.top != 0) {
+ mBlinkDisplayOff = !mBlinkDisplayOff;
+ invalidate(mBlinkRect);
+
+ if (!mHandler.hasMessages(MSG_BLINK)) {
+ mHandler.sendEmptyMessageDelayed(MSG_BLINK, FPS_MS);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private Bitmap getResBitmap(int bmpResId) {
Options opts = new Options();
opts.inDither = false;
Resources res = getResources();
Bitmap bmp = BitmapFactory.decodeResource(res, bmpResId, opts);
+
+ if (bmp == null && isInEditMode()) {
+ // BitmapFactory.decodeResource doesn't work from the rendering
+ // library in Eclipse's Graphical Layout Editor. Use this workaround instead.
+
+ Drawable d = res.getDrawable(bmpResId);
+ int w = d.getIntrinsicWidth();
+ int h = d.getIntrinsicHeight();
+ bmp = Bitmap.createBitmap(w, h, Config.ARGB_8888);
+ Canvas c = new Canvas(bmp);
+ d.setBounds(0, 0, w - 1, h - 1);
+ d.draw(c);
+ }
+
return bmp;
}
}
diff --git a/samples/TicTacToeMain/AndroidManifest.xml b/samples/TicTacToeMain/AndroidManifest.xml
index 4881b359b..ea3ac1138 100755
--- a/samples/TicTacToeMain/AndroidManifest.xml
+++ b/samples/TicTacToeMain/AndroidManifest.xml
@@ -30,7 +30,7 @@
-
+
diff --git a/samples/TicTacToeMain/README.txt b/samples/TicTacToeMain/README.txt
new file mode 100755
index 000000000..005285f48
--- /dev/null
+++ b/samples/TicTacToeMain/README.txt
@@ -0,0 +1,48 @@
+Sample: TicTacToeLib and TicTacToeMain.
+
+--------
+Summary:
+--------
+
+These two projects work together. They demonstrate how to use the ability to
+split an APK into multiple projects.
+
+Build is supported both via Ant (command-line tools) or via ADT (the Android
+plugin for Eclipse).
+
+--------
+Details:
+--------
+
+TicTacToeMain is the main project. It defines a main activity that is first
+displayed to the user. When one of the start buttons is selected, an
+activity defined in TicTacToeLib is started.
+
+To define that TicTacToeMain uses TicTacToeLib as a "project library", the
+file TicTacToeMain/default.properties contains the special line:
+ android.library.reference.1=../TicTacToeLib/
+
+
+TicTacToeLib is the "project library". It can contain both source code (.java)
+and Android resources (anything under /res) that will be merged in the final
+APK. To define this is a library, the file TicTacToeLib/default.project
+contains the special line:
+ android.library=true
+
+
+One important thing to realize is that the library is not a separately-compiled
+JAR file: the source and resources from the library are _actually_ merged in
+the main project and the result is used to generate the APK. This means that
+the main project can either use or redefine behavior from the libraries.
+
+
+To use the main vs livrary project:
+- In ADT, just open import both projects and launch the main project.
+- In Ant, use 'android update project' to create the build files and set the SDK location,
+ and then run 'ant debug' on the main project.
+
+
+For more details on the purpose of this feature, its limitations and detailed usage,
+please read the SDK guide at
+
+
diff --git a/samples/TicTacToeMain/default.properties b/samples/TicTacToeMain/default.properties
index d2fdd807a..7702ab664 100755
--- a/samples/TicTacToeMain/default.properties
+++ b/samples/TicTacToeMain/default.properties
@@ -24,5 +24,6 @@
# project structure.
android.library.reference.1=../TicTacToeLib/
-# Project target.
+# Project target. This requires the tools from SDK Froyo (API 8) to be used.
+# STOPSHIP change to target=8 once Froyo API is set to 8.
target=android-Froyo
diff --git a/samples/TicTacToeMain/res/values/strings.xml b/samples/TicTacToeMain/res/values/strings.xml
index 00522f703..436877fd4 100755
--- a/samples/TicTacToeMain/res/values/strings.xml
+++ b/samples/TicTacToeMain/res/values/strings.xml
@@ -21,6 +21,5 @@
Welcome to the Tic-Tac-Toe Sample!
This sample code demonstrates how to split an application in multiple projects by using the \'project library\' available in the Froyo SDK Tools.
This activity is defined in one project. The second activity, launched by one of the buttons below, is located in another project which is a \"library\" to the main one and merged in the same APK.
- Hello World, MainActivity!
Tic-Tac-Toe Sample
diff --git a/samples/TicTacToeMain/src/com/example/tictactoe/MainActivity.java b/samples/TicTacToeMain/src/com/example/tictactoe/MainActivity.java
index a66b54b53..b8d4afeb2 100755
--- a/samples/TicTacToeMain/src/com/example/tictactoe/MainActivity.java
+++ b/samples/TicTacToeMain/src/com/example/tictactoe/MainActivity.java
@@ -16,6 +16,7 @@
package com.example.tictactoe;
import com.example.tictactoe.library.GameActivity;
+import com.example.tictactoe.library.GameView.State;
import android.app.Activity;
import android.content.Intent;
@@ -47,7 +48,8 @@ public class MainActivity extends Activity {
private void startGame(boolean startWithHuman) {
Intent i = new Intent(this, GameActivity.class);
- i.putExtra(GameActivity.EXTRA_START_WITH_HUMAN, startWithHuman);
+ i.putExtra(GameActivity.EXTRA_START_PLAYER,
+ startWithHuman ? State.PLAYER1.getValue() : State.PLAYER2.getValue());
startActivity(i);
}
}
\ No newline at end of file