Wednesday, April 20, 2011

Passing multiple rows into oracle PL/SQL procedure

Introduction
Oracle off course uses standard Java database connectivity interfaces for communication with it's database. This is all nice and good, but if you know that you will use only Oracle database for your project, maybe it is useful to use some Oracle specific Java libraries in you project.

In this tutorial I will represent some specific Oracle Java libraries that can help you pass structured data into PL/SQL stored procedures. This structured data can contain multiple rows (each row contain multiple columns). So how can you do that?

Implementation

We will use two oracle specific classes:
oracle.sql.ARRAY
and
oracle.sql.STRUCT

First class (ARRAY) help us creating array of STRUCT object. STRUCT object is created by using static oracle.sql.StructDescriptor.createDescriptor method. This method uses existing Object type from database.
ARRAY object is created by using oracle.sql.ArrayDescriptor.createDescriptor method. This method uses existing collection type from database. This collection elements are of type that is same as our object type (I hope you understand this :) - This is basically "array of our object type" defined on database as collection type).

I create simple helper class to handle creation of structure and call to PL/SQL procedure. Here it is...

import java.sql.Connection;

import oracle.jbo.client.Configuration;
import oracle.jbo.server.DBTransaction;

import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
import oracle.sql.STRUCT;
import oracle.sql.StructDescriptor;

public class TransferStructure {

    private StructDescriptor structureDescriptor;
    private ArrayDescriptor arrayDescriptor;
    private Object[] structValues;
    private int fieldsCount;
    private Connection connection;
    private STRUCT[] allRows;
    private String[] structDescColumnNames;
    private DBTransaction dbTransaction;
    private int currentRowIndx = 0;

    public TransferStructure(String objectTypeName, String arrayTypeName,
                             int numberOfRows,
                             DBTransaction dbTransaction) throws Exception {
        this.dbTransaction = dbTransaction;
        this.connection =
                Call_Pl_SqlCodeUtil.getCurrentConnection(dbTransaction);
        allRows = new STRUCT[numberOfRows];

        structureDescriptor =
                StructDescriptor.createDescriptor(objectTypeName, this.connection);
        arrayDescriptor =
                ArrayDescriptor.createDescriptor(arrayTypeName, this.connection);
        structValues = new Object[fieldsCount];
        fieldsCount = structureDescriptor.getMetaData().getColumnCount();
        structDescColumnNames = new String[fieldsCount];

    }

    public void initializeRow() throws Exception {
        structValues = new Object[fieldsCount];        
    }
    
    public void setFiledValue(String fieldName,
                              Object value) throws Exception {
        structValues[fieldPos(fieldName)] = value;
    }
    
    /**
     * Sadly but you need to call this at the end of row populatio.
     * @throws Exception
     */
    public void finalizeRow() throws Exception {
      STRUCT struct =
          new STRUCT(structureDescriptor, this.connection, structValues);
      allRows[currentRowIndx++] = struct;
    }
    
    /**
     * Finds field position in structure.
     * @param fieldName
     * @return
     * @throws Exception
     */
    private int fieldPos(String fieldName) throws Exception {
        int fieldPosition = -1;

        for (int i = 1; i < fieldsCount + 1; i++) {
            String currentField =
                structureDescriptor.getMetaData().getColumnName(i);
            if (currentField.equals(fieldName)) {
                fieldPosition = i - 1;
                break;
            }
        }
        return fieldPosition;
    }

    private ARRAY getArrayStructure() throws Exception {
        return new ARRAY(arrayDescriptor, this.connection, allRows);
    }

    public void makeProcedureCall(String procedureName) throws Exception {
        Call_Pl_SqlCodeUtil.callStoredProcedure(dbTransaction, procedureName,
                                                new Object[] { getArrayStructure() });
    }
}
You can use this helper class like so:
public static void main(String[] args) throws Exception {

        TransferStructure ts =
            new TransferStructure("YOUR_OBJECT_TYPE", "YOUR_ARRAY_TYPE_OF_OBJECTS",
                                  2, "getDatabaseConnection");
        ts.initializeRow();
        ts.setFiledValue("YOUR_FIRST_ATTRIBUTE", "value1");
        ts.finalizeRow();
        ts.initializeRow();
        ts.setFiledValue("YOUR_SECOND_ATTRIBUTE", new oracle.jbo.domain.Number(1234));
        ts.finalizeRow();
        ts.makeProcedureCall("YOUR_PACKAGE.your_procedure_with_in_parameter(?)");

}
You will also need method to call stored procedure from Java. This is not so hard to find on net. Also I want to note that this example is mainly implemented to run on Oracle ADF framework, but I think it will also run on some non-oracle development environments if you acquire necessary libraries.

Tuesday, April 19, 2011

Design patterns in Java - Decorator

Introduction


As holy grail of Java OO design book (GoF - Elements Of Reusable OO Software) say: "Decorator design pattern is intended to add additional responsibility to an object dynamically. This design pattern is also known as Wrapper".

So in another words this design pattern enable object inheritance at runtime. It achieve this by using interface inheritance and class composition.

Implementation

In Java classes java.io package also use decorator design pattern. For example:

File f = new file("d:\something.txt");
//Here FileInputStream "inherits" File class.
FileInputStream is = new FileInputStream(f);
//Here we add buffering capability to FileInputStream using decorator.
BufferedInputStream bis = new BufferedInputStream(fi);

I must point out that Decorator design pattern can also be used to remove responsibility from object (our example will not include this case).

Following code represent simple implementation of this pattern in Java.

Interface that will be our default "object". This object will be decorated (extended).
/**
 * Basic phone that can do only basic ringing.
 */
public interface IPhone {
    
    void announceCall();

    void announceMessage();

}
Decorator interface that extends our default "object" interface. So instead of static inheritance in implementation we create interface inheritance.
/**
 * Decorator that adds additional functionality to basic Phone.
 */
public interface IPhoneDecorator extends IPhone {

    void vibrate(int milliseconds);

    void flashLED();
}
Concrete implementation of our "default" object (nothing interesting, just simple interface implementation).
public class Phone implements IPhone {

    /*
     * This Phone announce call only by ringing.
     */
    public void announceCall() {
        System.out.println("Ring!");
    }

    /*
     * Phone announce message by makeing some sound.
     */
    public void announceMessage() {
        System.out.println("Make a sound!");
    }
}
Concrete decorator implementation that implements all operations. It delegate part of functionality to our "default" object and add more functionality of his own. It has one filed named phone which represent our "default" object implementation to which we will delegate stuff.
/**
 * Concrete decorator class that adds additional functionality.
 * @author jan.krizan
 */
public class UpgradePhoneDecorator implements IPhoneDecorator {

    private IPhone phone;

    /**
     * Constructor through which client put
     * instance that will be decorated.
     * @param phone
     */
    public UpgradePhoneDecorator(IPhone phone) {
        super();
        this.phone = phone;
    }

    public void vibrate(int milliseconds) {
        System.out.println("Vibrating for " + milliseconds + " milliseconds.");
    }

    public void flashLED() {
        System.out.println("Make a LED flash!");
    }

    public void announceCall() {
        //Delegate basic functionality to Phone clase.
        phone.announceCall();
        //Add additional functionality.
        vibrate(1500);
    }

    public void announceMessage() {
        //Delegate basic functionality to Phone clase.
        phone.announceMessage();
        //Add additional functionality.
        flashLED();
    }
}
We can now see what can our "default" object do before and after it has been decorated. You can see that we provide phone instance to decorator constructor and by using polymorphism (dynamic binding) we delegate basic work to our "default" object.
public static void main(String[] args) {
        IPhone oldPhone = new Phone();
        System.out.println("What can old phone do!");
        oldPhone.announceCall();
        oldPhone.announceMessage();
        System.out.println();
        IPhoneDecorator decoratedPhone = new UpgradePhoneDecorator(oldPhone);
        System.out.println("What can new phone do!");
        decoratedPhone.announceCall();
        decoratedPhone.announceMessage();
    }

Hope you like this!


Here is output (example uses ANT):
run:
What can old phone do!
Ring!
Make a sound!

What can new phone do!
Ring!
Vibrating for 1500 milliseconds.
Make a sound!
Make a LED flash!
BUILD SUCCESSFUL (total time: 0 seconds)

Wednesday, April 13, 2011

Faces generator for Oracle ADF (ver. 0.5)

Introduction


Hello everyone. I wish to present you one small project that I worked on in spare time (which I don't have plenty). The ultimate purpose of this "generator project" is to create JDeveloper plugin that will generate complete view controller project for Oracle ADF using just model project. This generator will enable programmer to create complete view controller (or several view controllers) project(s) in any state of application model project. So for example we can in about half an hour create model from database schema and create complete view controller based on that model. Then we can show that prototype to our customer and let him to decide what are necessary changes we need to perform.

Beside prototyping this can also help us in overcome many problems that JDeveloper programming has to offer. :) I know what you may be thinking: "Yes I think this idea is similar to existing Oracle product named HeadStart". But  this generator is flexible and can be customized to organization needs and to organization usual development methods for Oracle ADF. Also I think that programmers like (go figure) to program, so they will not just be clicking but they will create business logic and unit tests on model and that is important. You can also leverage existing skills inside your organization using generator and there is practically no learning curve as opposite to HeadStart.

You can watch how generator work by watching these two videos on youtube:


Hope you like it, full source code for project that is used in demonstration can be download from here.

Thursday, April 7, 2011

Complete Android game

Introduction

When developing android games you can decide to use OpenGL (for 3D games development) or Java drawing interfaces for simpler 2D games.
In this example I will show you how you can develop android 2D game. This game is similar to game "hexxagon" which I played a long ago on my old DOS box (eh...memories: 386SX, 2MB RAM, Trident 512KB, 42MB HDD, BASIC, TURBO PASCAL...that was the times!).

Main goal in this game is to have as many as possible balls at the end. There are following rules in this game: You have balls and you can move them or you can copy them (only to length of 3 places). Will the ball perform move or copy depends on distance that it need to travel. Only if distance is 1 then ball is copied. You perform move into destination empty field. Every opponent ball that is around destination empty field will be "transformed"
to you ball color.

Because I understand that this explanation is not so great I made simple video example on youtube and please see how it works, hope it will help you to better understand basic game logic (at least it is better than this horrible description :) ).


Class design

I put many comments in code to make it more readable and useful and if you are interested in details please look there. I will here only explain main class organization and design decisions. You can download full source code from my google code repository.
Please note that this is really far away from any finished or polished code. But enough with excuses let's begin!

We separated game implementation into two main classes. First class (BoardView) is custom view that is responsible for animation, drawing and for handling user input (touch on screen). Second class is main activity class and is responsible for game flow, saving state, restoring state and initialization. This is also only activity in this game. There is also third less important class that handle game logic.

There was strong motivation to separate view from game-flow controller in this implementation. BoardView class handle only stuff that is closely coupled with animation and drawing while activity (MinMaxBalls) handle game logic-flow.

I must admit something. The title of game can confuse experienced programmer that this game implementation uses MinMax algorithm to calculate next move. In first iteration of game implementation this algorithm was present but it use too much of CPU power and in real life we cannot predict that user will choose best move possible so algorithm perform strangely when user choose to perform illogical moves. So I decided that is best to exclude it for now. I will do another blog post about this algorithm but in some simpler scenario game (maybe tic tac toe or something like that).

MinMaxBalls main activity

Either way, here is the code for MinMaxBalls main activity:
package org.codingwithpassion.minmaxballs;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import org.codingwithpassion.minmaxballs.BoardView.MoveStageListener;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Main activity for game. It is really unnecessary complex and
 * need to be refactored at some point in future.
 * @author jan.krizan
 */
public class MinMaxBalls extends Activity {

 // This is actual location where we store state of board. It is here because
 // of save instance state stuff and decoupling between view/controler and model. 
 // So this activity is something like model.
 private State[][] positions = new State[BoardView.BOARD_SIZE][BoardView.BOARD_SIZE];

 // Game instance that is used mainly for calculating computer moves.
 private Game game = new Game();
 
 // Two TextView views that we use for tracking current score.
 private TextView humanScore;
 private TextView computerScore;
 
 // Dialog and menu id-s.
 private static final int DIALOG_RESET = 1;
 private static final int DIALOG_ABOUT = 2;
 private static final int MENU_RESET = 3;
 private static final int MENU_ABOUT = 4;

 // Intent parameter id.
 private static final String DIFFICULTY = "minmaxdifficulty";
  
 private boolean isFinish = false;

 private NumberFormat numberFormat = new DecimalFormat("00");
 
 // Instance of our view.
 private BoardView boardView;

 /*
  * OK, main activity, nothing clever.
  */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  setContentView(R.layout.main);

  boardView = (BoardView) findViewById(R.id.boardView);
  humanScore = (TextView) findViewById(R.id.humanScore);
  computerScore = (TextView) findViewById(R.id.compScore);

  boardView.setFocusable(true);
  boardView.setFocusableInTouchMode(true);
  boardView.setMoveStageListener(new CellSelected());
  
  // Initialize positions.
  Game.setEmptyValues(positions);
  Game.setSolidSquares(positions);
  Game.initializeStartPositions(positions);
  
  // Initial game difficulty.
  int difficulty = Game.MEDIUM_DIFFICULTY;

  Bundle extras = getIntent().getExtras();
  if (extras != null) {
   difficulty = Integer.parseInt(extras.getString(DIFFICULTY));
   Log.d("difficult", String.valueOf(difficulty));
  }
  
  // We set difficulty to know what kind (which "hard-nest") of moves to perform.
  game.setDifficulty(difficulty);
  
  // Bind positions table to View values.
  boardView.setPositions(positions);
    
  refreshScore();
 }
 
 /*
  * Simple but effective refresh score.
  */
 private void refreshScore() {
  humanScore.setText(numberFormat.format(Game.countPlayerBalls(positions,
    State.HUMAN)));
  computerScore.setText(numberFormat.format(Game.countPlayerBalls(
    positions, State.COMP)));
 }
 
 @Override
 protected Dialog onCreateDialog(int id) {
  if (id == DIALOG_RESET) {
   String easy = String
     .valueOf(this.getText(R.string.difficulty_easy));
   String hard = String
     .valueOf(this.getText(R.string.difficulty_hard));
   String medium = String.valueOf(this
     .getText(R.string.difficulty_medium));

   final CharSequence[] items = { easy, medium, hard };

   AlertDialog.Builder builder = new AlertDialog.Builder(this);
   builder.setTitle(R.string.game_difficulty);
   builder.setItems(items, new DialogInterface.OnClickListener() {
    public void onClick(DialogInterface dialog, int item) {
     String difficulty = String.valueOf(Game.MEDIUM_DIFFICULTY);
     if (item == 0) {
      difficulty = String.valueOf(Game.EASY_DIFFICULTY);
     } else if (item == 1) {
      difficulty = String.valueOf(Game.MEDIUM_DIFFICULTY);
     } else if (item == 2) {
      difficulty = String.valueOf(Game.HARD_DIFFICULTY);
     }
     Intent intent = getIntent();
     intent.putExtra(DIFFICULTY, difficulty);
     finish();
     startActivity(intent);
    }
   });
   return builder.create();
  } else if (id == DIALOG_ABOUT) {      
   final TextView message = new TextView(this);   
   final SpannableString s = new SpannableString(
     this.getText(R.string.blog_link));
   Linkify.addLinks(s, Linkify.WEB_URLS);
   message.setText(s);
   message.setMovementMethod(LinkMovementMethod.getInstance());
   AlertDialog.Builder builder = new AlertDialog.Builder(this);   
   builder.setView(message)
     .setCancelable(true)
     .setIcon(android.R.drawable.stat_notify_error)
     .setNegativeButton("OK",
       new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog,
          int id) {
         dialog.cancel();
        }
       });
   return builder.create();
  }
  return null;
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  super.onCreateOptionsMenu(menu);

  int groupId = 0;

  MenuItem menuItemReset = menu.add(groupId, MENU_RESET, Menu.NONE,
    R.string.new_game);
  menuItemReset.setIcon(android.R.drawable.star_big_on);
  MenuItem menuItemAbout = menu.add(groupId, MENU_ABOUT, Menu.NONE,
    R.string.about);
  menuItemAbout.setIcon(android.R.drawable.stat_notify_error);

  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  super.onOptionsItemSelected(item);

  if (item.getItemId() == MENU_RESET) {
   showDialog(DIALOG_RESET);
   return true;
  } else if (item.getItemId() == MENU_ABOUT) {
   showDialog(DIALOG_ABOUT);
   return true;
  }

  return false;
 }

 private void showToast(int message) {
  Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG)
    .show();
 }

 @Override
 public void onSaveInstanceState(Bundle bundle) {
  super.onSaveInstanceState(bundle);

  if (game.getMove().player == State.HUMAN) {
   if (!isFinish) {
    int source_i = game.getMove().sourceMove.i;
    int source_j = game.getMove().sourceMove.j;
    if (source_i != -1 && source_j != -1) {
     if (positions[source_i][source_j] == State.SELECTED) {
      positions[source_i][source_j] = State.HUMAN;
     }
    }
    game.bestMove(positions, State.COMP);
    game.getMove().player = State.COMP;
    performMove(game.getMove(), false);
    changecolors(game.getMove(), false);
   }
  }

  for (int i = 0; i < positions.length; i++) {
   int[] pos = new int[positions.length];
   for (int j = 0; j < positions.length; j++) {
    pos[j] = positions[i][j].getValue();
   }
   bundle.putIntArray("pos" + i, pos);
  }
 }

 @Override
 public void onRestoreInstanceState(Bundle bundle) {
  for (int i = 0; i < BoardView.BOARD_SIZE; i++) {
   for (int j = 0; j < BoardView.BOARD_SIZE; j++) {
    positions[i][j] = State
      .fromInt(bundle.getIntArray("pos" + i)[j]);
   }
  }
  refreshScore();
 }
 
 /*
  * OK, the real meat of game-flow control. It uses semaphore-like
  * poor design to control flow and it is crucially important to
  * preserve order of if-s and boolean fields checking.
  * Very, very poor design (dragons live here kind of design!) so:
  * TODO: Please refactor this I get pretty nasty poor design smell here!
  */
 private class CellSelected implements MoveStageListener {
  
  // Semaphore-like boolean fields.
  private boolean isCompMove = false;
  private boolean isCompSelected = false;

  private boolean isHumanBallChange = false;
  private boolean isCompBallsChange = false;

  private boolean isCompVictory = false;
  private boolean isHumanVictory = false;
  
  /* 
   * React on user click on the board. If user clicks on her/his
   * ball then select that ball, of she/he select empty field then
   * move ball, else display error by displaying error animation
   * on that square.
   */
  public void userClick(int i, int j) {
   Coordinate humanSource = game.getMove().sourceMove;
   if (!isFinish && positions[i][j] == State.HUMAN) {
    game.getMove().player = State.HUMAN;
    // If we already selected ball and now we change our mind.
    if (humanSource.isNotEmpty()) {
     positions[humanSource.i][humanSource.j] = State.HUMAN;
    }
    positions[i][j] = State.SELECTED;
    boardView.selectBall(i, j, State.SELECTED);
    humanSource.i = i;
    humanSource.j = j;
   } else if (!isFinish
     && humanSource.isNotEmpty()
     && positions[i][j] == State.EMPTY
     && Game.isAllowedDistance(i, j, humanSource.i,
       humanSource.j, 2)) {
    game.getMove().destinationMove.i = i;
    game.getMove().destinationMove.j = j;
    performMove(game.getMove(), true);
    isHumanBallChange = true;
   } else {
    boardView.error(i, j);
    isHumanBallChange = false;
   }

  }
  
  /*
   * If animation is complete, then it is obvious we need to do something.
   * What will be done is decided by checking various boolean fiels.
   */
  public void animationComplete() {
   // TODO: refactor:
   // Excessive conditional logic, must preserve conditions order of 
   // conditions...bad, bad design. :(
   if (isCompVictory) {
    changeAllColors(State.HUMAN, State.COMP);
    isCompVictory = false;
    // stop all activity
    isCompSelected = false;
   }
   if (isHumanVictory) {
    changeAllColors(State.COMP, State.HUMAN);
    isHumanVictory = false;
    // stop all activity
    isCompSelected = false;
   }

   if (isCompBallsChange) {
    changecolors(game.getMove(), true);
    isCompBallsChange = false;
    refreshScore();
    game.deleteMove();
    checkWin();
   }

   if (isCompMove) {

    performMove(game.getMove(), true);
    isCompMove = false;
    isCompBallsChange = true;
   }
   if (isCompSelected) {

    // Calculate move for computer.
    game.bestMove(positions, State.COMP);
    game.getMove().player = State.COMP;
    Coordinate compSelect = game.getMove().sourceMove;
    boardView.selectBall(compSelect.i, compSelect.j, State.SELECTED);
    positions[compSelect.i][compSelect.j] = State.SELECTED;
    isCompSelected = false;
    isCompMove = true;
   }

   if (isHumanBallChange) {
    changecolors(game.getMove(), true);
    isHumanBallChange = false;
    isCompSelected = true;
    refreshScore();
    game.deleteMove();
    checkWin();
   }
  }

  private void checkWin() {
   if (game.isWin(positions, State.HUMAN)) {
    checkWhoWin();
   }
   if (game.isWin(positions, State.COMP)) {
    checkWhoWin();
   }
  }

  private void checkWhoWin() {
   if (Game.countPlayerBalls(positions, State.HUMAN) >= Game
     .countPlayerBalls(positions, State.COMP)) {
    isHumanVictory = true;
    showToast(R.string.human_wins);
    isCompVictory = false;
    isFinish = true;
   } else {
    isCompVictory = true;
    showToast(R.string.comp_wins);
    isHumanVictory = false;
    isFinish = true;
   }
  }
 }
 
 private void changecolors(Move move, boolean withAnimation) {
  State toWho = move.player;
  State fromWho = toWho == State.HUMAN ? State.COMP : State.HUMAN;
  boolean[][] changeThese = Game.changeColors(positions,
    move.destinationMove.i, move.destinationMove.j, fromWho);

  for (int i = 0; i < BoardView.BOARD_SIZE; i++) {
   for (int j = 0; j < BoardView.BOARD_SIZE; j++) {
    if (changeThese[i][j]) {
     positions[i][j] = toWho;
    }
   }
  }
  if (withAnimation)
   boardView.changeColors(changeThese, fromWho, toWho);
 }

 private void changeAllColors(State fromWho, State toWho) {
  boolean[][] changeThese = Game.changeAllColors(positions, fromWho);

  for (int i = 0; i < BoardView.BOARD_SIZE; i++) {
   for (int j = 0; j < BoardView.BOARD_SIZE; j++) {
    if (changeThese[i][j]) {
     positions[i][j] = toWho;
    }
   }
  }

  boardView.changeColors(changeThese, fromWho, toWho);

 }

 private void performMove(Move move, boolean withAnimation) {
  int start_i = move.sourceMove.i;
  int start_j = move.sourceMove.j;
  int dest_i = move.destinationMove.i;
  int dest_j = move.destinationMove.j;

  State who = move.player;
  if (Game.getDistance(start_i, start_j, dest_i, dest_j) > 1) {
   if (withAnimation)
    boardView.moveBall(start_i, start_j, dest_i, dest_j, who);
   positions[start_i][start_j] = State.EMPTY;
  } else {
   if (withAnimation)
    boardView.createBall(dest_i, dest_j, who);
   positions[start_i][start_j] = who;
  }
  positions[dest_i][dest_j] = who;
 }

}
You can see that there is one inner class named CellSelected. It's responsibility is to control game flow and to handle "messages" from view that are send by method calls. It also save instance field data and perform additional moves when is necessary (when user tilt screen for example) in it's onSaveInstanceState method.

BoardView View class

The second interesting class is BoardView class.
package org.codingwithpassion.minmaxballs;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.os.Handler;
import android.os.Message;
import android.os.Handler.Callback;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Implementation of board view/controller. It draws board, handle user events
 * (touch), rotating screen and animation.
 * 
 * @author jan.krizan
 */
public class BoardView extends View {

 public static final int BOARD_MARGIN = 10;
 public static final int BOARD_SIZE = 7;
 public static final int GRID_SIZE = 2;

 private static final int MSG_ANIMATE = 0;

 private final Handler animationHandler = new Handler(
   new AnimationMessageHandler());

 private MoveStageListener moveStageListener;

 /**
  * Listener interface that send messages to Activity. Activity then handle
  * this messages.
  */
 public interface MoveStageListener {

  // Fires when user click's somewhere on board.
  void userClick(int i, int j);

  // When animation complete at same current move stage is complete.
  void animationComplete();
 }

 public void setMoveStageListener(MoveStageListener selectionListener) {
  this.moveStageListener = selectionListener;
 }

 /**
  * Animation interface that control animation handler.
  */
 public interface Animation {
  // This is called on onDraw method.
  void animate(Canvas canvas);

  // Say if animation should end.
  boolean isFinish();

  // Control which cells will be animated and hence should be
  // ignored when we draw grid.
  boolean skip(int i, int j);

  // How much frames per second we will use for our animation.
  int fps();
 }

 private Animation animation = new NullAnimation();

 // Here we store animation board state with all players and intermediate
 // states for cells.
 private State[][] positions;

 public void setPositions(State[][] positions) {
  this.positions = positions;
 }

 // Paint for board table line. It is here because onPaint is
 // using it several time per frame.
 private Paint boardLinePaint;

 // Width of board is also calculated dynamically when screen
 // size changes.
 private float boardWidth;

 // Maximum radius of ball - calculated dynamically also...
 private float maxRadius;

 // Can freely be here because it is calculated every time screen size
 // changes.
 private float cellSize;

 public BoardView(Context context, AttributeSet attrs) {
  super(context, attrs);
  requestFocus();
  boardLinePaint = new Paint();
  boardLinePaint.setColor(0xFFFFFFFF);
  boardLinePaint.setStrokeWidth(GRID_SIZE);
  boardLinePaint.setStyle(Style.STROKE);
 }

 /*
  * Classic onDraw. It paints table and ball states. When we need to animate
  * stuff we call it to refresh canvas state (easy as in classic Java 2D
  * graphics animation).
  */
 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  float offsetBoardWidth = boardWidth - BOARD_MARGIN;
  canvas.drawRect(BOARD_MARGIN, BOARD_MARGIN, offsetBoardWidth,
    offsetBoardWidth, boardLinePaint);

  for (int i = 0; i < BOARD_SIZE; i++) {
   float cellStep = BOARD_MARGIN + (i * cellSize);
   canvas.drawLine(cellStep, BOARD_MARGIN, cellStep, offsetBoardWidth,
     boardLinePaint);
   canvas.drawLine(BOARD_MARGIN, cellStep, offsetBoardWidth, cellStep,
     boardLinePaint);
  }

  setValuesFromDatas(canvas);

  animation.animate(canvas);

 }

 /*
  * Set values from board state structure and skip animated items.
  */
 private void setValuesFromDatas(Canvas canvas) {
  for (int i = 1; i < BOARD_SIZE + 1; i++) {
   for (int j = 1; j < BOARD_SIZE + 1; j++) {
    // If this are currently animated squares, do not
    // draw them!
    if (!animation.skip(i - 1, j - 1))
     drawBall(i, j, positions[i - 1][j - 1], maxRadius, canvas,
       255);
    drawSolidSquare(canvas, i, j, positions[i - 1][j - 1]);
   }
  }
 }

 /*
  * Method for drawing filled square (when user touch inappropriate section
  * of table). It is stupid to create Paint object every time, but it is here
  * for readability and encapsulation reasons.
  */
 private void drawWhiteSquare(Canvas canvas, int i, int j, int alpha) {
  Paint paint = new Paint();
  paint.setColor(Color.WHITE);
  paint.setStyle(Style.FILL);
  paint.setAlpha(alpha);
  drawCustomRect(i, j, canvas, paint, 0);
 }

 private void drawCustomRect(int i, int j, Canvas canvas, Paint paint,
   float shrink) {
  canvas.drawRect(i * cellSize + GRID_SIZE + BOARD_MARGIN + shrink, j
    * cellSize + GRID_SIZE + BOARD_MARGIN + shrink, (i + 1)
    * cellSize - GRID_SIZE + BOARD_MARGIN - shrink, (j + 1)
    * cellSize + BOARD_MARGIN - GRID_SIZE - shrink, paint);
 }

 /*
  * Draw fancy "disabled" and solid square. Same story here for Paint object
  * as in drawWhiteSquare method.
  */
 private void drawSolidSquare(Canvas canvas, int i, int j, State who) {
  if (who == State.BLOCK) {

   Paint paintBigger = new Paint();
   paintBigger.setColor(0xFFA800A8);
   paintBigger.setStyle(Style.FILL);

   drawCustomRect(i - 1, j - 1, canvas, paintBigger, 0);

   Paint paintSmaller = new Paint();
   paintSmaller.setColor(0xFFFC54FC);
   paintSmaller.setStyle(Style.FILL);

   float shrink = cellSize * 0.15f;

   drawCustomRect(i - 1, j - 1, canvas, paintSmaller, shrink);

   canvas.drawLine((i - 1) * cellSize + GRID_SIZE + BOARD_MARGIN,
     (j - 1) * cellSize + GRID_SIZE + BOARD_MARGIN, (i - 1)
       * cellSize + GRID_SIZE + BOARD_MARGIN + shrink,
     (j - 1) * cellSize + GRID_SIZE + BOARD_MARGIN + shrink,
     paintSmaller);

   canvas.drawLine(i * cellSize - GRID_SIZE + BOARD_MARGIN, (j - 1)
     * cellSize + GRID_SIZE + BOARD_MARGIN, i * cellSize
     - GRID_SIZE + BOARD_MARGIN - shrink, (j - 1) * cellSize
     + GRID_SIZE + BOARD_MARGIN + shrink, paintSmaller);

   canvas.drawLine(i * cellSize - GRID_SIZE + BOARD_MARGIN, j
     * cellSize - GRID_SIZE + BOARD_MARGIN, i * cellSize
     - GRID_SIZE + BOARD_MARGIN - shrink, j * cellSize
     - GRID_SIZE + BOARD_MARGIN - shrink, paintSmaller);

   canvas.drawLine((i - 1) * cellSize + GRID_SIZE + BOARD_MARGIN, j
     * cellSize - GRID_SIZE + BOARD_MARGIN, (i - 1) * cellSize
     + GRID_SIZE + BOARD_MARGIN + shrink, j * cellSize
     - GRID_SIZE + BOARD_MARGIN - shrink, paintSmaller);
  }

 }

 /*
  * Draw custom balls. We can change balls alpha and radius in animation.
  */
 private void drawBall(int i, int j, State who, float radius, Canvas canvas,
   int alpha) {

  // Calculate where we will put ball in our grid based on coordinates in
  // grid.
  float x = cellSize * (i - 1) + cellSize / 2 + BOARD_MARGIN;
  float y = cellSize * (j - 1) + cellSize / 2 + BOARD_MARGIN;
  // Skip empty every time.
  if (who != State.EMPTY && who != State.BLOCK) {
   Paint smallBall = new Paint();

   int color = Color.RED;
   if (who == State.SELECTED)
    color = Color.BLACK;
   else if (who == State.COMP)
    color = Color.BLUE;
   smallBall.setColor(color);
   smallBall.setStyle(Style.FILL);
   smallBall.setAlpha(alpha);

   Paint bigBall = new Paint();
   bigBall.setColor(Color.WHITE);
   bigBall.setStyle(Style.FILL);
   bigBall.setAlpha(alpha);

   // Smaller ball is 15% smaller than bigger.
   canvas.drawCircle(x, y, radius * 1.15f, bigBall);

   canvas.drawCircle(x, y, radius, smallBall);
  }
 }

 /*
  * Select ball action operation (ball become black).
  */
 public void selectBall(int i, int j, State who) {
  animation = new PutBall();
  PutBall putBall = (PutBall) animation;
  putBall.alpha = 0;
  putBall.i = i;
  putBall.j = j;
  putBall.who = State.SELECTED;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 /*
  * Create new ball operation (on empty square in grid).
  */
 public void createBall(int i, int j, State who) {
  animation = new CreateBallAnimation();
  CreateBallAnimation createBallAnimation = (CreateBallAnimation) animation;
  createBallAnimation.radius = 0;
  createBallAnimation.i = i;
  createBallAnimation.j = j;
  createBallAnimation.who = who;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 /*
  * Paint square in white block operation (along with alpha animation) when
  * user perform illegal move.
  */
 public void error(int i, int j) {
  animation = new FillSquareAnimation();
  FillSquareAnimation fillSquareAnimation = (FillSquareAnimation) animation;
  fillSquareAnimation.i = i;
  fillSquareAnimation.j = j;
  fillSquareAnimation.alpha = 255;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 /*
  * Move ball from one place to another operation (with animation also).
  */
 public void moveBall(int i1, int j1, int i2, int j2, State who) {
  animation = new MoveBallsAnimation();
  MoveBallsAnimation createBallAnimation = (MoveBallsAnimation) animation;
  createBallAnimation.radius = maxRadius;
  createBallAnimation.moveFrom[i1][j1] = true;
  createBallAnimation.moveTo[i2][j2] = true;
  createBallAnimation.whoFrom = who;
  createBallAnimation.whoTo = who;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 /*
  * Change colors for all balls operation that have same coordinates as true
  * values in "changeThem" matrix. Animation is same as for move operation.
  */
 public void changeColors(boolean[][] changeThem, State whoFrom, State whoTo) {
  animation = new MoveBallsAnimation();
  MoveBallsAnimation createBallAnimation = (MoveBallsAnimation) animation;
  createBallAnimation.radius = maxRadius;
  createBallAnimation.moveFrom = changeThem;
  createBallAnimation.moveTo = changeThem;
  createBallAnimation.whoFrom = whoFrom;
  createBallAnimation.whoTo = whoTo;

  animationHandler.sendEmptyMessage(MSG_ANIMATE);
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  if (animation.isFinish()) {
   int action = event.getAction();

   int i = (int) ((event.getX() - BOARD_MARGIN) / cellSize);
   int j = (int) ((event.getY() - BOARD_MARGIN) / cellSize);

   if (i >= 0 && i <= (BOARD_SIZE - 1) && j >= 0
     && j <= (BOARD_SIZE - 1)) {

    // If user just click, then we will show painted square.
    if (action == MotionEvent.ACTION_DOWN) {
     moveStageListener.userClick(i, j);
     return true;
    }
   }
  }

  return false;
 }

 /*
  * Recalculate fields based on current screen size.
  */
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  boardWidth = w < h ? w : h;
  cellSize = (boardWidth - GRID_SIZE * BOARD_MARGIN) / BOARD_SIZE;

  maxRadius = cellSize * 0.68f / 2;
 }

 /*
  * Set dimension of current view.
  */
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  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);
 }

 /**
  * Inner animation handler. This handler call itself several times during
  * animation and in every pass invalidates current view (calls onDraw method
  * of View). It is controlled by Animation interface and hence concrete
  * implementation of Animation interface. This implementation "tells" it
  * when to stop.
  */
 private class AnimationMessageHandler implements Callback {
  public boolean handleMessage(Message msg) {
   if (msg.what == MSG_ANIMATE) {
    BoardView.this.invalidate();
    if (!animationHandler.hasMessages(MSG_ANIMATE)) {
     if (animation.isFinish()) {
      animationHandler.removeMessages(MSG_ANIMATE);
      moveStageListener.animationComplete();
     } else {
      animationHandler.sendEmptyMessageDelayed(MSG_ANIMATE,
        animation.fps());
     }
    }
    return true;
   }
   return false;
  }
 }

 /**
  * This animation doesn't do anything - null animation.
  */
 private class NullAnimation implements Animation {
  public void animate(Canvas canvas) {
   // do nothing
  }

  public boolean isFinish() {
   return true;
  }

  public boolean skip(int i, int j) {
   return false;
  }

  public int fps() {
   return 1000 / 1;
  }
 }

 /**
  * Create ball animation (balls pops-up up in empty square).
  */
 private class CreateBallAnimation implements Animation {

  public int i;
  public int j;
  public State who;
  public float radius;

  public void animate(Canvas canvas) {
   drawBall(i + 1, j + 1, who, radius, canvas, 255);
   radius += 8;
   if (radius >= BoardView.this.maxRadius)
    radius = BoardView.this.maxRadius;
  }

  public boolean isFinish() {
   return radius >= BoardView.this.maxRadius;
  }

  public boolean skip(int i, int j) {
   return (this.i == i && this.j == j);
  }

  public int fps() {
   return 1000 / 16;
  }
 }

 /**
  * Move ball animation that moves current ball from one square to another
  * altogether with pop-ing-up effect. :) It can be use for one ball or ball
  * set (represented by coordinate matrix).
  */
 private class MoveBallsAnimation implements Animation {
  public boolean[][] moveFrom = new boolean[BOARD_SIZE][BOARD_SIZE];
  public boolean[][] moveTo = new boolean[BOARD_SIZE][BOARD_SIZE];
  public State whoFrom;
  public State whoTo;
  public float radius;

  public boolean firstPahseFinish;
  public boolean secondPhaseFinish;

  public void animate(Canvas canvas) {
   if (!firstPahseFinish) {
    for (int i = 0; i < BOARD_SIZE; i++) {
     for (int j = 0; j < BOARD_SIZE; j++) {
      if (moveFrom[i][j])
       drawBall(i + 1, j + 1, whoFrom, radius, canvas, 255);
     }
    }

    radius -= 8;
    if (radius <= 0) {
     radius = 0;
     firstPahseFinish = true;
    }
   } else {

    for (int i = 0; i < BOARD_SIZE; i++) {
     for (int j = 0; j < BOARD_SIZE; j++) {
      if (moveTo[i][j])
       drawBall(i + 1, j + 1, whoTo, radius, canvas, 255);
     }
    }

    radius += 8;
    if (radius >= maxRadius) {
     radius = maxRadius;
     secondPhaseFinish = true;
    }
   }
  }

  public boolean isFinish() {
   return firstPahseFinish && secondPhaseFinish;
  }

  public boolean skip(int i, int j) {
   return moveFrom[i][j] || moveTo[i][j];
  }

  public int fps() {
   return 1000 / 16;
  }
 }

 /**
  * Paint square with white gradually disappeared white inner square.
  */
 private class FillSquareAnimation implements Animation {

  public int i;
  public int j;

  public int alpha;

  public void animate(Canvas canvas) {
   drawWhiteSquare(canvas, i, j, alpha);
   alpha -= 75;
   if (alpha <= 0)
    alpha = 0;
  }

  public boolean isFinish() {
   return alpha <= 0;
  }

  public boolean skip(int i, int j) {
   return false;
  }

  public int fps() {
   return 1000 / 16;
  }
 }
 
 /**
  * And last but not the least animation that gradually change ball
     * color.  
  */
 private class PutBall implements Animation {

  public int i;
  public int j;
  public State who;
  public int alpha;

  public void animate(Canvas canvas) {
   drawBall(i + 1, j + 1, who, maxRadius, canvas, alpha);
   alpha += 100;
   if (alpha >= 255)
    alpha = 255;
  }

  public boolean isFinish() {
   return alpha >= 255;
  }

  public boolean skip(int i, int j) {
   return (this.i == i && this.j == j);
  }

  public int fps() {
   return 1000 / 16;
  }
 }
}
This class extends View and overrides onDraw method. This is rudimentary animation implementation in android and it uses Java 2D interfaces so that means that it is almost same as Core Java 2D animation.

It defined two interfaces. MoveStageListener interface listener is used for sending messages to activity (MinMaxBalls) class. Second interface Animation is used to send messages to Message Handler. Message Handler is used to perform animation loop and to invalidate view (so it calls onDraw method). We can do this also using another thread, but in this way we have slightly cleaner code.

Animation interfaces "tells" Message Handler when to stop, how much fps it should use and stuff like that. We have several implementation of this Animation interfaces and all perform some simple animations.

And that's it. Please feel free to put comments if you want ask/suggest anything and I will do as much as I can to notice that comments (It is hard to notice comments on blog, because blogger do not send emails when someone put comment -- they shoud!) :) And yes, I will also try to answer them.