Implementation of the tic-tac-toe sample.

Added some README.txt

Change-Id: Ie2703ffdfdeba773d0fd27153ec0cdb806b2e2dc
diff --git a/samples/TicTacToeLib/AndroidManifest.xml b/samples/TicTacToeLib/AndroidManifest.xml
index cc667ee..9772b88 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">
-    <application>
-        <activity android:name="GameActivity" />
-    </application>
 </manifest>
\ No newline at end of file
diff --git a/samples/TicTacToeLib/README.txt b/samples/TicTacToeLib/README.txt
new file mode 100755
index 0000000..a43b2f1
--- /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 4ac577c..89927d7 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 0000000..c0f5cba
--- /dev/null
+++ b/samples/TicTacToeLib/res/layout-land/lib_game.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:gravity="center_vertical|center_horizontal"
+    >
+
+    <com.example.tictactoe.library.GameView
+        android:id="@+id/game_view"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layout_margin="20dip"
+        />
+
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        >
+
+        <TextView
+            android:id="@+id/info_turn"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:layout_marginBottom="10dip"
+            />
+
+        <Button
+            android:id="@+id/next_turn"
+            android:text="I'm done"
+            android:minEms="10"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="20dip"
+            android:layout_marginRight="20dip"
+            />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/samples/TicTacToeLib/res/layout/lib_game.xml b/samples/TicTacToeLib/res/layout/lib_game.xml
index c4e674d..75c284a 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"
     >
 
     <com.example.tictactoe.library.GameView
         android:id="@+id/game_view"
-        android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
         android:layout_margin="20dip"
         android:layout_weight="1"
         />
@@ -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"
         />
 
     <Button
-        android:id="@+id/info_turn"
+        android:id="@+id/next_turn"
+        android:text="I'm done"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_marginLeft="20dip"
         android:layout_marginRight="20dip"
-        android:layout_weight="1"
         />
 
 </LinearLayout>
diff --git a/samples/TicTacToeLib/res/values/strings.xml b/samples/TicTacToeLib/res/values/strings.xml
index 5707400..9fd8413 100755
--- a/samples/TicTacToeLib/res/values/strings.xml
+++ b/samples/TicTacToeLib/res/values/strings.xml
@@ -16,6 +16,5 @@
 -->
 
 <resources>
-
-
+    <!-- TODO externalize strings here. -->
 </resources>
diff --git a/samples/TicTacToeLib/src/com/example/tictactoe/library/GameActivity.java b/samples/TicTacToeLib/src/com/example/tictactoe/library/GameActivity.java
index f75665e..e1514cd 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 @@
         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 2c27b50..89f8584 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.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 @@
 

 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 @@
         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 @@
         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 @@
         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 @@
         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();

 

-            // TODO

+            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();

+                }

+            }

+

             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;

     }

 }