Blame src/com/icystar/findnumber/Game.java

1b8111
package com.icystar.findnumber;
1b8111

1b8111
import java.io.FileInputStream;
1b8111
import java.io.FileOutputStream;
1b8111
import java.io.IOException;
1b8111
import java.util.ArrayList;
1b8111
import java.util.List;
1b8111
import java.util.Random;
1b8111
import java.util.Timer;
1b8111
import java.util.TimerTask;
1b8111

1b8111
import android.content.Context;
1b8111
import android.graphics.Canvas;
1b8111
import android.graphics.Paint;
1b8111
import android.graphics.Point;
1b8111
import android.graphics.Rect;
1b8111
import android.graphics.Typeface;
1b8111
import android.text.TextPaint;
1b8111
import android.util.FloatMath;
1b8111
import android.view.View;
1b8111
import android.widget.TextView;
1b8111

1b8111
public class Game {
1b8111
	public static Game[] games = new Game[] { new Game(), new Game() };
1b8111

1b8111
	public static class Number {
1b8111
		public int number;
1b8111
		public String text;
1b8111
		public int digits;
1b8111
		public int sizeIndex;
1b8111
		public float size;
1b8111
		public float scaleX;
1b8111
		public int fontIndex;
1b8111
		Typeface font;
1b8111
		public Rect rect = new Rect();
1b8111
		public Point position = new Point();
1b8111
		
1b8111
		public void save(FileOutputStream fos) throws IOException {
1b8111
			Serializer.writeInt(fos, number);
1b8111
			Serializer.writeFloat(fos, size);
1b8111
			Serializer.writeFloat(fos, scaleX);
1b8111
			Serializer.writeInt(fos, fontIndex);
1b8111
			Serializer.writeInt(fos, rect.left);
1b8111
			Serializer.writeInt(fos, rect.right);
1b8111
			Serializer.writeInt(fos, rect.top);
1b8111
			Serializer.writeInt(fos, rect.bottom);
1b8111
			Serializer.writeInt(fos, position.x);
1b8111
			Serializer.writeInt(fos, position.y);
1b8111
		}
1b8111

1b8111
		public void load(FileInputStream fis) throws IOException {
1b8111
			number 		= Serializer.readInt(fis);
1b8111
			size 		= Serializer.readFloat(fis);
1b8111
			scaleX 		= Serializer.readFloat(fis);
1b8111
			fontIndex 	= Serializer.readInt(fis);
1b8111
			rect.left 	= Serializer.readInt(fis);
1b8111
			rect.right 	= Serializer.readInt(fis);
1b8111
			rect.top 	= Serializer.readInt(fis);
1b8111
			rect.bottom = Serializer.readInt(fis);
1b8111
			position.x 	= Serializer.readInt(fis);
1b8111
			position.y 	= Serializer.readInt(fis);
1b8111
			
1b8111
			text = Integer.toString(number);
1b8111
			digits = text.length();
1b8111
			sizeIndex = 0;
1b8111
			font = fonts[fontIndex];
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	public interface Listener {
1b8111
		public void onLock(Game game);
1b8111
		public void onWin(Game game, float time);
1b8111
		public void onTouchNumber(Game game, int touchedNumber, int maxNumber, boolean win);
1b8111
	}
1b8111

1b8111
	private static final String filename = "game.sav";
1b8111
	private static final int sizesCount = 100;
1b8111
	private static final int generationTrials = 100;
1b8111
	private static final int fontTrials = 100;
1b8111
	private static final int positionTrials = 100;
1b8111
	public static final Typeface[] fonts = new Typeface[] {
1b8111
		Typeface.create(Typeface.SERIF, Typeface.NORMAL),
1b8111
		Typeface.create(Typeface.SERIF, Typeface.BOLD),
1b8111
		Typeface.create(Typeface.SERIF, Typeface.ITALIC),
1b8111
		Typeface.create(Typeface.SERIF, Typeface.BOLD_ITALIC),
1b8111
		Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL),
1b8111
		Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD),
1b8111
		Typeface.create(Typeface.SANS_SERIF, Typeface.ITALIC),
1b8111
		Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD_ITALIC)
1b8111
	};
1b8111
	
1b8111
	private static Object staticHandle = new Object();
1b8111
	private static boolean stateLoaded = false;
1b8111
	private static String[] playerNames;
1b8111
	
1b8111
	private int width = 0;
1b8111
	private int height = 0;
1b8111
	private float minSize; 
1b8111
	private float maxSize; 
1b8111
	private int initialMaxSizeIndex; 
1b8111
	private int maxDigits; 
1b8111
	private Rect[][][] sizes;
1b8111

1b8111
	private TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
1b8111
	private TextView numberTextView;
1b8111
	private TextView timeTextView;
1b8111
	private TextView playerNameTextView;
1b8111
	private NumbersView numbersView;
1b8111
	private View lockView;
1b8111
	private Listener listener;
1b8111
	
1b8111
	private Timer timer;
1b8111
	private Object handle = new Object();
1b8111
	private int waitToGenerate = 0;
1b8111
	
1b8111
	private List<point> invalidTouches = new ArrayList<point>();
</point></point>
1b8111

1b8111
	// serialization data
1b8111
	private Number[] numbers = new Number[0];
1b8111
	private int currentNumberIndex = -1;
1b8111
	private int currentNumber = 0;
1b8111
	private float time = 0;
1b8111
	private float lockTime = 0;
1b8111
	private boolean paused = true;
1b8111
	private boolean playing = false;
1b8111
	private boolean isWin = false;
1b8111
	private boolean isLose = false;
1b8111
	
1b8111
	public void save(FileOutputStream fos) throws IOException {
1b8111
		synchronized (handle) {
1b8111
			Serializer.writeInt(fos, currentNumberIndex);
1b8111
			Serializer.writeInt(fos, Math.round(time));
1b8111
			Serializer.writeInt(fos, Math.round(lockTime));
1b8111
			Serializer.writeInt(fos, paused ? 1 : 0);
1b8111
			Serializer.writeInt(fos, playing ? 1 : 0);
1b8111
			Serializer.writeInt(fos, numbers.length);
1b8111
			for(Number number : numbers)
1b8111
				number.save(fos);
1b8111
		}
1b8111
	}
1b8111

1b8111
	public void load(FileInputStream fis) throws IOException {
1b8111
		synchronized (handle) {
1b8111
			currentNumberIndex	= Serializer.readInt(fis);
1b8111
			time				= Serializer.readInt(fis);
1b8111
			lockTime			= Serializer.readInt(fis);
1b8111
			paused				= Serializer.readInt(fis) != 0;
1b8111
			playing				= Serializer.readInt(fis) != 0;
1b8111
			numbers = new Number[Serializer.readInt(fis)];
1b8111
			for(int i = 0; i < numbers.length; i++) {
1b8111
				numbers[i] = new Number();
1b8111
				numbers[i].load(fis);
1b8111
			}
1b8111
			currentNumber = currentNumberIndex >= 0 && currentNumberIndex < numbers.length
1b8111
			              ? numbers[currentNumberIndex].number : 0; 
1b8111
		}
1b8111
	}
1b8111
	
1b8111

1b8111
	Game() {
1b8111
		timer = new Timer();
1b8111
		timer.schedule(new TimerTask() {
1b8111
			@Override
1b8111
			public void run() { onTime(1); }
1b8111
		}, 1000, 1000);
1b8111
	}
1b8111
	
1b8111
	private float internalCalcSize(int index) {
1b8111
		return minSize + (maxSize - minSize)*(float)index/(float)(sizesCount-1);
1b8111
	}
1b8111
	
1b8111
	private void internalCalcSizes(int count) {
1b8111
		maxSize = Math.min(width, height)/2f;
1b8111
		minSize = count < 50 ? maxSize/5f : maxSize/10f;
1b8111
		initialMaxSizeIndex = sizesCount/2;
1b8111
		if (count > 25) initialMaxSizeIndex = sizesCount/4;
1b8111
		if (count > 50) initialMaxSizeIndex = sizesCount/6;
1b8111
		if (initialMaxSizeIndex < 1) initialMaxSizeIndex = 1;
1b8111

1b8111
		String template = "MMMMMMMMMMMMMMMMMMMMMMMMMMMMM";
1b8111
		sizes = new Rect[fonts.length][sizesCount][maxDigits + 1];
1b8111
		paint.setTextScaleX(1f);
1b8111
		for(int fontIndex = 0; fontIndex < fonts.length; fontIndex++) {
1b8111
			paint.setTypeface(fonts[fontIndex]);
1b8111
			for(int sizeIndex = 0; sizeIndex < sizesCount; sizeIndex++) {
1b8111
				paint.setTextSize(internalCalcSize(sizeIndex));
1b8111
				for(int digits = 0; digits <= maxDigits; digits++) {
1b8111
					sizes[fontIndex][sizeIndex][digits] = new Rect();
1b8111
					paint.getTextBounds(template, 0, digits, sizes[fontIndex][sizeIndex][digits]);
1b8111
				}
1b8111
			}
1b8111
		}
1b8111
	}
1b8111

1b8111
	private boolean internalCheckRect(Rect rect, int count, int exclude) {
1b8111
		if (rect.left < 0 || rect.top < 0 || rect.right > width || rect.bottom > height)
1b8111
			return false;
1b8111
		for(int i = 0; i < count; i++)
1b8111
			if (Rect.intersects(rect, numbers[i].rect))
1b8111
				return false;
1b8111
		return true;
1b8111
	}
1b8111
	
1b8111
	private void internalGenerate(int count) {
1b8111
		waitToGenerate = 0;
1b8111
		time = 0;
1b8111
		lockTime = 0;
1b8111
		playing = false;
1b8111
		isWin = false;
1b8111
		isLose = false;
1b8111
		if (timer != null) timer.cancel();
1b8111
		timer = null;
1b8111
		
1b8111
		maxDigits = count < 10 ? 1 : (count < 100 ? 2 : (count < 1000 ? 3 : 4));
1b8111
		internalCalcSizes(count);
1b8111
		
1b8111
		numbers = new Number[count];
1b8111
		Random random = new Random();
1b8111
		
1b8111
		// fill digits
1b8111
		int[] remainingNumbers = new int[count];
1b8111
		for(int i = 0; i < count; i++) remainingNumbers[i] = i+1;
1b8111
		for(int i = 0; i < count; i++) {
1b8111
			int j = random.nextInt(count - i);
1b8111
			numbers[i] = new Number();
1b8111
			numbers[i].number = remainingNumbers[j];
1b8111
			numbers[i].text = Integer.toString(numbers[i].number);
1b8111
			numbers[i].digits = numbers[i].text.length();
1b8111
			for(int k = j+1; k < count-i; k++) remainingNumbers[k-1] = remainingNumbers[k];
1b8111
		}
1b8111

1b8111
		// generate numbers
1b8111
		boolean success = false;
1b8111
		for(int generationTry = 0; !success && generationTry < generationTrials; generationTry++) {
1b8111
			for(int i = 0; i < count; i++) {
1b8111
				success = false;
1b8111
				Number number = numbers[i];
1b8111
				for(int fontTry = 0; !success && fontTry < fontTrials && fontTry < initialMaxSizeIndex; fontTry++) {
1b8111
					number.fontIndex = random.nextInt(fonts.length);
1b8111
					number.sizeIndex = random.nextInt(initialMaxSizeIndex - fontTry);
1b8111
					number.font = fonts[number.fontIndex];
1b8111
					number.size = internalCalcSize(number.sizeIndex);					
1b8111
					number.scaleX = 1f; //random.nextFloat()*1.5f + 0.5f;
1b8111
					Rect r = sizes[number.fontIndex][number.sizeIndex][number.digits];
1b8111
					int w = Math.round((r.right - r.left)*number.scaleX);
1b8111
					int h = r.bottom - r.top;
1b8111
					if (w < width && h < height) {
1b8111
						for(int positionTry = 0; !success && positionTry < positionTrials; positionTry++) {
1b8111
							int x = random.nextInt(width - w);
1b8111
							int y = random.nextInt(height - h);
1b8111
							number.rect.set(x, y, x+w, y+h);
1b8111
							number.position.set(number.rect.left - r.left, number.rect.top - r.top);
1b8111
							if (internalCheckRect(number.rect, i, -1))
1b8111
								success = true;
1b8111
						}
1b8111
					}
1b8111
				}
1b8111
				if (!success) break;
1b8111
			}
1b8111
		}
1b8111
		
1b8111
		if (success) {
1b8111
			// enlarge numbers
1b8111
			boolean enlarged = false;
1b8111
			Rect[] rects = new Rect[] { new Rect(), new Rect(), new Rect(), new Rect() }; 
1b8111
			while (enlarged) {
1b8111
				enlarged = true;
1b8111
				for(int i = 0; i < count; i++) {
1b8111
					Number number = numbers[i];
1b8111
					if (number.sizeIndex < sizesCount) {
1b8111
						Rect r = sizes[number.fontIndex][number.sizeIndex+1][number.digits];
1b8111
						int w = Math.round((r.right - r.left)*number.scaleX);
1b8111
						int h = r.bottom - r.top;
1b8111
						int validCount = 0;
1b8111
						for(int j = 0; j < 3; j++) {
1b8111
							int x = j % 2 == 0 ? number.rect.left : number.rect.right - w; 
1b8111
							int y = j / 2 == 0 ? number.rect.top : number.rect.bottom - h;
1b8111
							rects[validCount].set(x, y, x+w, y+h);
1b8111
							if (internalCheckRect(rects[validCount], count, i)) validCount++;
1b8111
						}
1b8111
						if (validCount > 0) {
1b8111
							number.sizeIndex++;
1b8111
							number.size = internalCalcSize(number.sizeIndex);
1b8111
							number.rect.set(rects[random.nextInt(validCount)]);
1b8111
							number.position.set(number.rect.left - r.left, number.rect.top - r.top);
1b8111
							enlarged = true;
1b8111
						}
1b8111
					}
1b8111
				}
1b8111
			}
1b8111
		} else {
1b8111
			numbers = new Number[0];
1b8111
		}
1b8111
		
1b8111
		time = 0;
1b8111
		lockTime = 0;
1b8111
		playing = true;
1b8111
		internalSetCurrentNumber(1);
1b8111
		internalUpdateViews();
1b8111
		timer = new Timer();
1b8111
		timer.schedule(new TimerTask() {
1b8111
			@Override
1b8111
			public void run() { onTime(1); }
1b8111
		}, 1000, 1000);
1b8111
	}
1b8111
	
1b8111
	private static boolean checkDistance(int x0, int y0, int x1, int y1, int distSqr)
1b8111
		{ return (x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) < distSqr; }
1b8111
	
1b8111
	private boolean internalCheckTouch(int x, int y) {
1b8111
		Number number = null;
1b8111
		try { number = numbers[currentNumberIndex]; } catch (Exception e) { }
1b8111
		if (number != null) {
1b8111
			int radius = Math.min(width, height)/20;
1b8111
			int radiusSqr = radius*radius;
1b8111
			Rect rect = number.rect;
1b8111

1b8111
			if (new Rect(rect.left - radius, rect.top, rect.right + 2*radius, rect.bottom ).contains(x, y)
1b8111
			 || new Rect(rect.left, rect.top - radius, rect.right, rect.bottom + 2*radius ).contains(x, y)
1b8111
			 || checkDistance(x, y, rect.left, rect.top, radiusSqr) 
1b8111
			 || checkDistance(x, y, rect.right, rect.top, radiusSqr) 
1b8111
			 || checkDistance(x, y, rect.left, rect.bottom, radiusSqr) 
1b8111
			 || checkDistance(x, y, rect.right, rect.bottom, radiusSqr) 
1b8111
			) {
1b8111
				return true;
1b8111
			}
1b8111
		}
1b8111
		return false;
1b8111
	}
1b8111
	
1b8111
	private void internalSetCurrentNumber(int value) {
1b8111
		currentNumberIndex = -1;
1b8111
		currentNumber = 0;
1b8111
		for(int i = 0; i < numbers.length; i++)
1b8111
			if (numbers[i].number == value)
1b8111
				{ currentNumberIndex = i; currentNumber = value; break; }
1b8111
		internalUpdateNumberTextView();
1b8111
	}
1b8111

1b8111
	private void internalUpdateNumberTextView() {
1b8111
		if (numberTextView != null) {
1b8111
			int numbersCount = numbers.length; 
1b8111
			int numbersFound = isWin ? numbersCount : currentNumber - 1;
1b8111
			int id = R.string.currentNumber;
1b8111
			if (isLose) id = R.string.currentNumberLose;
1b8111
			if (isWin) { id = R.string.currentNumberWin; numbersFound = numbersCount; } 
1b8111
			
1b8111
			final TextView textView = numberTextView;
1b8111
			final String text = textView.getContext().getString(id, currentNumber, numbersFound, numbersCount);
1b8111
			textView.post(new Runnable() { @Override public void run() { textView.setText(text); } });
1b8111
		}
1b8111
	}
1b8111

1b8111
	private void internalUpdateTimeTextView() {
1b8111
		if (timeTextView != null) {
1b8111
			int min = (int)FloatMath.floor(time/60);
1b8111
			int sec = (int)FloatMath.floor(time - min*60);
1b8111
			String minStr = Integer.toString(min);
1b8111
			String secStr = Integer.toString(sec);
1b8111
			while (minStr.length()  < 2) minStr = "0" + minStr;
1b8111
			while (secStr.length()  < 2) secStr = "0" + secStr;
1b8111

1b8111
			final TextView textView = timeTextView;
1b8111
			final String text = minStr + ":" + secStr;
1b8111
			textView.post(new Runnable() { @Override public void run() { textView.setText(text); } });
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	private void internalUpdatePlayerNameTextView() {
1b8111
		if (playerNameTextView != null) {
1b8111
			final TextView textView = playerNameTextView;
1b8111
			final int visibility = playing ? View.VISIBLE : View.GONE;
1b8111
			textView.post(new Runnable() { @Override public void run() { textView.setVisibility(visibility); } });
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	private void internalUpdateNumbersView() {
1b8111
		if (numbersView != null) {
1b8111
			final NumbersView view = numbersView;
1b8111
			view.post(new Runnable() { @Override public void run() { view.invalidate(); } });
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	private void internalUpdateLockView() {
1b8111
		if (lockView != null) {
1b8111
			final View view = lockView;
1b8111
			final int visibility = lockTime <= 0 ? View.GONE : View.VISIBLE;
1b8111
			view.post(new Runnable() { @Override public void run() {
1b8111
				if (view.getVisibility() != visibility) view.setVisibility(visibility);
1b8111
				if (visibility == View.VISIBLE) view.invalidate();
1b8111
			} });
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	private void internalUpdateViews() {
1b8111
		internalUpdateNumberTextView();
1b8111
		internalUpdateTimeTextView();
1b8111
		internalUpdatePlayerNameTextView();
1b8111
		internalUpdateNumbersView();
1b8111
		internalUpdateLockView();
1b8111
	}
1b8111

1b8111
	
1b8111
	
1b8111
	public void start(int count) {
1b8111
		synchronized (handle) {
1b8111
			if (width > 0 && height > 0)
1b8111
				internalGenerate(count);
1b8111
			else
1b8111
				waitToGenerate = count;
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	public void stop() {
1b8111
		synchronized (handle) {
1b8111
			playing = false;
1b8111
			isWin = false;
1b8111
			isLose = false;
1b8111
			numbers = new Number[0];
1b8111
			internalUpdateNumberTextView();
1b8111
			internalUpdatePlayerNameTextView();
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	public void lose() {
1b8111
		synchronized (handle) {
1b8111
			playing = false;
1b8111
			isWin = false;
1b8111
			isLose = true;
1b8111
			internalUpdateNumberTextView();
1b8111
			internalUpdatePlayerNameTextView();
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	public void win() {
1b8111
		synchronized (handle) {
1b8111
			playing = false;
1b8111
			isWin = true;
1b8111
			isLose = false;
1b8111
			internalUpdateNumberTextView();
1b8111
			internalUpdatePlayerNameTextView();
1b8111
		}
1b8111
	}
1b8111

1b8111
	public static void lock() {
1b8111
		for(Game game : games) {
1b8111
			synchronized (game.handle) {
1b8111
				game.lockTime = 5;
1b8111
				game.internalUpdateLockView();
1b8111
			}
1b8111
			if (game.listener != null) game.listener.onLock(game);
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	public boolean getPlaying()
1b8111
		{ synchronized (handle) { return playing; } }
1b8111
	public boolean getPaused()
1b8111
		{ synchronized (handle) { return paused; } }
1b8111
	public void setPaused(boolean value)
1b8111
		{ synchronized (handle) { paused = value; } }
1b8111
	public int getCount()
1b8111
		{ synchronized (handle) { return numbers.length; } }
1b8111
	
1b8111
	public int getCurrentNumber() {
1b8111
		synchronized (handle) {
1b8111
			try { return numbers[currentNumberIndex].number; } catch (Exception e) { }
1b8111
			return 0;
1b8111
		}
1b8111
	}
1b8111

1b8111
	public void onLayout(int width, int height) {
1b8111
		synchronized (handle) {
1b8111
			this.width = width;
1b8111
			this.height = height;
1b8111
			if (width > 0 && height > 0 && waitToGenerate > 0)
1b8111
				internalGenerate(waitToGenerate);
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	public void onTouch(int x, int y) {
1b8111
		boolean touch = false;
1b8111
		int touchedNumber = 0;
1b8111
		int maxNumber = 0;
1b8111

1b8111
		boolean win = false;
1b8111
		float winTime = 0;
1b8111
		
1b8111
		synchronized (handle) {
1b8111
			if (playing && !paused && lockTime <= 0) {
1b8111
				if (internalCheckTouch(x, y)) {
1b8111
					touch = true;
1b8111
					touchedNumber = currentNumber;
1b8111
					maxNumber = numbers.length;
1b8111
					if (currentNumber < numbers.length) {
1b8111
						internalSetCurrentNumber(currentNumber + 1);
1b8111
					} else {
1b8111
						this.win();
1b8111
						win = true;
1b8111
						winTime = time;
1b8111
					}
1b8111
					invalidTouches.clear();
1b8111
				} else {
1b8111
					invalidTouches.add(new Point(x, y));
1b8111
					if (invalidTouches.size() > 1) {
1b8111
						int maxDist = Math.min(width, height)/4;
1b8111
						int places = 1;
1b8111
						Point min = new Point(width, height);
1b8111
						Point max = new Point(0, 0);
1b8111
						for(Point p : invalidTouches) {
1b8111
							min.x = Math.min(min.x, p.x);
1b8111
							min.y = Math.min(min.y, p.y);
1b8111
							max.x = Math.max(max.x, p.x);
1b8111
							max.y = Math.max(max.y, p.y);
1b8111
							if (max.x - min.x > maxDist || max.y - min.y > maxDist) {
1b8111
								places++;
1b8111
								min.set(p.x, p.y);
1b8111
								max.set(p.x, p.y);
1b8111
							}
1b8111
						}
1b8111
						if (places > 3) {
1b8111
							lock();
1b8111
							invalidTouches.clear();
1b8111
						}
1b8111
					}
1b8111
				}
1b8111
			}
1b8111
		}
1b8111
		
1b8111
		if (touch && listener != null) listener.onTouchNumber(this, touchedNumber, maxNumber, win);
1b8111
		if (win && listener != null) listener.onWin(this, winTime);
1b8111
	}
1b8111
	
1b8111
	public void onTime(float time) {
1b8111
		synchronized (handle) {
1b8111
			if (playing && !paused) {
1b8111
				this.time += time;
1b8111
				lockTime -= time;
1b8111
				if (lockTime <= 0) lockTime = 0;
1b8111
				internalUpdateTimeTextView();
1b8111
				internalUpdateLockView();
1b8111
				if (Math.round(this.time) % 3 == 0 && invalidTouches.size() > 0)
1b8111
					invalidTouches.remove(0);
1b8111
			}
1b8111
		}
1b8111
	}
1b8111

1b8111
	public void draw(Canvas canvas) {
1b8111
		synchronized (handle) {
1b8111
			for(int i = 0; i < numbers.length; i++) {
1b8111
				Number number = numbers[i];
1b8111
				paint.setTypeface(number.font);
1b8111
				paint.setTextSize(number.size);
1b8111
				paint.setTextScaleX(number.scaleX);
1b8111
				canvas.drawText(number.text, number.position.x, number.position.y, paint);
1b8111
			}
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	public void setListener(Listener value)
1b8111
		{ synchronized (handle) { listener = value; } }
1b8111
	public void setNumberTextView(TextView value)
1b8111
		{ synchronized (handle) { numberTextView = value; internalUpdateNumberTextView(); } }
1b8111
	public void setTimeTextView(TextView value)
1b8111
		{ synchronized (handle) { timeTextView = value; internalUpdateTimeTextView(); } }
1b8111
	public void setPlayerNameTextView(TextView value)
1b8111
		{ synchronized (handle) { playerNameTextView = value; internalUpdatePlayerNameTextView(); } }
1b8111
	public void setNumbersView(NumbersView value)
1b8111
		{ synchronized (handle) { numbersView = value; internalUpdateNumbersView(); } }
1b8111
	public void setLockView(View value)
1b8111
		{ synchronized (handle) { lockView = value; internalUpdateLockView(); } }
1b8111
	
1b8111
	private static void save(Context context) {
1b8111
		try {
1b8111
			FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE);
1b8111
			for(Game game : games) game.save(fos);
1b8111
			Serializer.writeInt(fos, playerNames.length);
1b8111
			if (playerNames == null) playerNames = new String[0];
1b8111
			for(String name : playerNames)
1b8111
				Serializer.writeString(fos, name);
1b8111
			ScoreboardActivity.save(fos);
1b8111
			Serializer.writeInt(fos, SoundPlayer.getEnabled() ? 1 : 0);
1b8111
		} catch (Exception e) { }
1b8111
	}
1b8111
	
1b8111
	private static void load(Context context) {
1b8111
		for(Game game : games) game.stop();
1b8111
		
1b8111
		try {
1b8111
			FileInputStream fis = context.openFileInput(filename);
1b8111
			for(Game game : games) game.load(fis);
1b8111
			playerNames = new String[Serializer.readInt(fis)];
1b8111
			for(int i = 0; i < playerNames.length; i++)
1b8111
				playerNames[i] = Serializer.readString(fis);
1b8111
			ScoreboardActivity.load(fis);
1b8111
			SoundPlayer.setEnabled(Serializer.readInt(fis) != 0);
1b8111
		} catch (Exception e) {
1b8111
			for(Game game : games) game.stop();
1b8111
			//e.printStackTrace();
1b8111
		}
1b8111
		if (playerNames == null) playerNames = new String[0];
1b8111
	}
1b8111
	
1b8111
	public static void saveState(Context context) {
1b8111
		synchronized (staticHandle) {
1b8111
			save(context);
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	public static void loadState(Context context) {
1b8111
		synchronized (staticHandle) {
1b8111
			if (!stateLoaded) {
1b8111
				stateLoaded = true;
1b8111
				load(context);
1b8111
			}
1b8111
		}
1b8111
	}
1b8111
	
1b8111
	public static int getMode() {
1b8111
		boolean g0 = games[0].getCount() > 0;
1b8111
		boolean g1 = games[1].getCount() > 0;
1b8111
		if (g0 && g1) return 2;	
1b8111
		if (g0 && !g1) return 1;
1b8111
		return 0;
1b8111
	}
1b8111
	
1b8111
	public static boolean getAnyPlaying() {
1b8111
		switch (getMode()) {
1b8111
		case 1:
1b8111
			return games[0].getPlaying();
1b8111
		case 2:
1b8111
			return games[0].getPlaying() || games[1].getPlaying();
1b8111
		}
1b8111
		return false;
1b8111
	}
1b8111
	
1b8111
	public static String getPlayerName(int index) {
1b8111
		synchronized (staticHandle) {
1b8111
			try { return playerNames[index] == null ? "" : playerNames[index]; } catch (Exception e) { }
1b8111
			return "";
1b8111
		}
1b8111
	}
1b8111

1b8111
	public static void setPlayerName(int index, String name) {
1b8111
		synchronized (staticHandle) {
1b8111
			if (name != null && name.length() > 0) {
1b8111
				try {
1b8111
					playerNames = Serializer.enlargeArrayIfNeed(String.class, playerNames, index+1);
1b8111
					playerNames[index] = name;
1b8111
				} catch (Exception e) {	}
1b8111
			}
1b8111
		}
1b8111
	}
1b8111
}