1 package de
.cwde
.freeshisen
;
3 import java
.lang
.ref
.WeakReference
;
5 import java
.util
.Locale
;
6 import java
.util
.Timer
;
7 import java
.util
.TimerTask
;
9 import android
.app
.Activity
;
10 import android
.app
.AlertDialog
;
11 import android
.content
.Context
;
12 import android
.content
.SharedPreferences
;
13 import android
.graphics
.Bitmap
;
14 import android
.graphics
.BitmapFactory
;
15 import android
.graphics
.Canvas
;
16 import android
.graphics
.Color
;
17 import android
.graphics
.Paint
;
18 import android
.graphics
.Paint
.Align
;
19 import android
.graphics
.Paint
.Cap
;
20 import android
.graphics
.Paint
.Join
;
21 import android
.graphics
.Paint
.Style
;
22 import android
.graphics
.Rect
;
23 import android
.graphics
.Typeface
;
24 import android
.os
.Handler
;
25 import android
.os
.Message
;
26 import android
.preference
.PreferenceManager
;
27 import android
.view
.MenuItem
;
28 import android
.view
.MotionEvent
;
29 import android
.view
.SurfaceHolder
;
30 import android
.view
.SurfaceView
;
32 class ShisenShoView
extends SurfaceView
implements SurfaceHolder
.Callback
{
34 private static final String INVALID_TIME
= "9:99:99";
35 private static final String COLOR_TEXT
= "#FFFFFF";
36 private static final String COLOR_TEXT_SHADOW
= "#000000";
37 private static final String COLOR_HINT
= "#F0C000";
38 private static final String COLOR_SELECTED
= "#FF0000";
40 private enum StatePlay
{ UNINITIALIZED
, IDLE
, SELECTED1
, SELECTED2
, GAMEOVER
};
41 private enum StatePaint
{ BOARD
, SELECTED1
, SELECTED2
, MATCHED
, WIN
, LOSE
, HINT
, TIME
};
43 private int screenWidth
;
44 private int screenHeight
;
45 private int tileHeight
;
46 private int tileWidth
;
48 private Point selection1
= new Point(0, 0);
49 private Point selection2
= new Point(0, 0);
50 private List
<Point
> path
= null;
51 private List
<Line
> pairs
= null;
52 private long startTime
;
53 private long playTime
;
54 private long baseTime
;
56 private Tileset tileset
;
58 static class hHandler
extends Handler
{
59 private final WeakReference
<ShisenShoView
> mTarget
;
61 hHandler(ShisenShoView target
) {
62 mTarget
= new WeakReference
<ShisenShoView
>(target
);
66 public void handleMessage(Message msg
) {
67 ShisenShoView target
= mTarget
.get();
69 target
.onUpdateTime();
73 private Handler timerHandler
= new hHandler(this);
75 private boolean timerRegistered
= false;
76 private ShisenSho app
;
77 private StatePlay cstate
;
78 private StatePaint pstate
;
79 private Canvas canvas
= null;
80 private SurfaceHolder surfaceHolder
= null;
81 private String time
= INVALID_TIME
;
83 public ShisenShoView(ShisenSho shisenSho
) {
84 super((Context
) shisenSho
);
86 cstate
= StatePlay
.UNINITIALIZED
;
87 surfaceHolder
= getHolder();
88 surfaceHolder
.addCallback(this);
89 tileset
= new Tileset(shisenSho
);
92 public ShisenShoView(Context ctx
) {
94 this.app
= (ShisenSho
) ctx
;
95 cstate
= StatePlay
.UNINITIALIZED
;
96 surfaceHolder
= getHolder();
97 surfaceHolder
.addCallback(this);
100 private void paint(StatePaint pstate
) {
105 private void control(StatePlay cstate
) {
109 private void loadBackground() {
110 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
111 ops
.inScaled
= false;
112 bg
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.kshisen_bgnd
, ops
);
113 bg
.setDensity(Bitmap
.DENSITY_NONE
);
116 private void registerTimer() {
118 return; // Already registered
120 timer
.scheduleAtFixedRate(new TimerTask() {
122 timerHandler
.sendEmptyMessage(Activity
.RESULT_OK
);
125 timerRegistered
= true;
128 private void unregisterTimer() {
130 return; // Already unregistered
133 timerRegistered
= false;
136 public void pauseTime() {
139 startTime
= System
.currentTimeMillis();
143 public void resumeTime() {
144 startTime
= System
.currentTimeMillis();
148 private void updateTime() {
149 if (cstate
!=StatePlay
.GAMEOVER
) {
150 playTime
= (System
.currentTimeMillis()-startTime
)/1000+baseTime
;
154 public void loadTileset() {
155 tileset
.loadTileset(screenWidth
, screenHeight
);
158 private void initializeGame() {
160 screenWidth
=getWidth();
161 screenHeight
=getHeight();
163 //undo.sensitive=false;
164 pstate
=StatePaint
.BOARD
;
166 control(StatePlay
.IDLE
);
167 startTime
=System
.currentTimeMillis();
170 if (!timerRegistered
) {
173 pairs
=app
.board
.getPairs(1);
176 public boolean onOptionsItemSelected(MenuItem item
) {
177 // Handle item selection
178 switch (item
.getItemId()) {
180 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
183 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
186 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
197 public void reset() {
198 control(StatePlay
.UNINITIALIZED
);
199 paint(StatePaint
.BOARD
);
202 private void onHintActivate() {
203 if (cstate
!=StatePlay
.GAMEOVER
) {
204 pairs
=app
.board
.getPairs(1);
205 paint(StatePaint
.HINT
);
207 paint(StatePaint
.BOARD
);
208 control(StatePlay
.IDLE
);
212 private void onUndoActivate() {
213 if (app
.board
.getCanUndo()) {
214 if (cstate
==StatePlay
.GAMEOVER
&& !timerRegistered
) {
215 // Reprogram the time update that had been
216 // deactivated with the game over status
220 paint(StatePaint
.BOARD
);
221 //undo.sensitive=app.board.getCanUndo();
222 control(StatePlay
.IDLE
);
226 public void onTimeCounterActivate() {
227 if (cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
228 // Reprogram the time update that had been
229 // deactivated with the time_counter=false
234 private void onUpdateTime() {
236 if (cstate
==StatePlay
.GAMEOVER
) {
241 @SuppressWarnings("deprecation")
242 public static void drawMessage(Canvas canvas
, int x
, int y
,
243 boolean centered
, String message
, float textSize
) {
244 Paint paint
= new Paint();
245 paint
.setLinearText(true);
246 paint
.setAntiAlias(true);
247 paint
.setTextAlign(centered ? Align
.CENTER
: Align
.LEFT
);
248 paint
.setTypeface(Typeface
.SANS_SERIF
);
249 paint
.setFakeBoldText(true);
250 paint
.setTextSize(textSize
);
251 paint
.setColor(Color
.parseColor(COLOR_TEXT_SHADOW
));
252 canvas
.drawText(message
, x
+ 1, y
+ 1, paint
);
253 paint
.setColor(Color
.parseColor(COLOR_TEXT
));
254 canvas
.drawText(message
, x
, y
, paint
);
257 public void repaint() {
258 if (surfaceHolder
== null) return;
260 if (canvas
== null) canvas
= surfaceHolder
.lockCanvas(null);
261 if (canvas
== null) return;
262 if (cstate
==StatePlay
.UNINITIALIZED
) initializeGame();
263 synchronized (surfaceHolder
) {
267 if (canvas
!= null) {
268 surfaceHolder
.unlockCanvasAndPost(canvas
);
274 protected void doDraw(Canvas canvas
) {
276 if (canvas
== null) return;
278 // Board upper left corner on screen
282 if (app
!=null && app
.board
!=null) {
283 x0
=(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2;
284 y0
=(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2;
287 int selectcolor
= Color
.parseColor(COLOR_SELECTED
);
288 int hintcolor
= Color
.parseColor(COLOR_HINT
);
290 // Background & board painting
300 // Background painting
301 int bgWidth
= bg
.getWidth();
302 int bgHeight
= bg
.getHeight();
303 for (int i
=0; i
<screenHeight
/bgHeight
+1; i
++) {
304 for (int j
=0; j
<screenWidth
/bgWidth
+1; j
++) {
305 canvas
.drawBitmap(bg
, j
*bgWidth
, i
*bgHeight
, null);
310 // Max visible size: 7x17
311 if (app
!=null && app
.board
!=null) {
312 for (int i
=0;i
<app
.board
.boardSize
[0];i
++) {
313 for (int j
=0;j
<app
.board
.boardSize
[1];j
++) {
314 // Tiles are 56px height, 40px width each
315 char piece
=app
.board
.board
[i
][j
];
317 canvas
.drawBitmap(tileset
.tile
[piece
], x0
+j
*tileWidth
, y0
+i
*tileHeight
, null);
325 // rectangle for selection 1
330 highlightTile(canvas
, x0
, y0
, selection1
, selectcolor
);
334 // rectangle for selection 2
338 highlightTile(canvas
, x0
, y0
, selection2
, selectcolor
);
347 for (Point p1
: path
) {
349 drawLine(canvas
, x0
, y0
, p0
, p1
, selectcolor
);
360 if (pairs
!= null && pairs
.size() > 0) {
361 Line pair
= pairs
.get(0);
364 path
= app
.board
.getPath(a
, b
);
366 highlightTile(canvas
, x0
, y0
, a
, hintcolor
);
370 for (Point p1
: path
) {
372 drawLine(canvas
, x0
, y0
, p0
, p1
, hintcolor
);
379 highlightTile(canvas
, x0
, y0
, b
, hintcolor
);
384 // Win & loose notifications
387 drawMessage(canvas
, screenWidth
/ 2, screenHeight
/ 2, true,
391 drawMessage(canvas
, screenWidth
/ 2, screenHeight
/ 2, true,
406 int hours
= (int) (playTime
/ (60 * 60));
407 int minutes
= (int) ((playTime
/ 60) % 60);
408 int seconds
= (int) (playTime
% 60);
410 time
= String
.format(Locale
.US
, "%01d:%02d:%02d",
411 hours
, minutes
, seconds
);
416 int timePosX
=screenWidth
-120;
417 int timePosY
=screenHeight
-10;
419 if (app
.timeCounter
) {
420 drawMessage(canvas
, timePosX
, timePosY
, false, time
, 30);
425 } catch (Exception e
) {
431 private void drawLine(Canvas canvas
, int x0
, int y0
, Point p0
, Point p1
, int color
) {
432 Paint paint
= new Paint();
433 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
434 paint
.setColor(color
);
435 paint
.setStyle(Style
.STROKE
);
436 paint
.setStrokeCap(Cap
.ROUND
);
437 paint
.setStrokeJoin(Join
.ROUND
);
438 paint
.setStrokeWidth(3);
440 x0
+ p0
.j
* tileWidth
- 2 + (tileWidth
/ 2),
441 y0
+ p0
.i
* tileHeight
- 2 + (tileHeight
/ 2),
442 x0
+ p1
.j
* tileWidth
- 2 + (tileWidth
/ 2),
443 y0
+ p1
.i
* tileHeight
- 2 + (tileHeight
/ 2), paint
);
446 private void highlightTile(Canvas canvas
, int x0
, int y0
, Point p
, int color
) {
447 Paint paint
= new Paint();
448 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
449 paint
.setColor(color
);
450 paint
.setStyle(Style
.STROKE
);
451 paint
.setStrokeCap(Cap
.ROUND
);
452 paint
.setStrokeJoin(Join
.ROUND
);
453 paint
.setStrokeWidth(3);
455 x0
+ p
.j
* tileWidth
- 2,
456 y0
+ p
.i
* tileHeight
- 2,
457 x0
+ p
.j
* tileWidth
+ tileWidth
+ 2,
458 y0
+ p
.i
* tileHeight
+ tileHeight
+ 2);
459 canvas
.drawRect(r
, paint
);
463 public boolean onTouchEvent(MotionEvent event
) {
464 if (event
.getAction()==MotionEvent
.ACTION_DOWN
) {
465 onClick(Math
.round(event
.getX()),Math
.round(event
.getY()));
467 return super.onTouchEvent(event
);
470 private void onClick(int x
, int y
) {
472 int i
=(y
-(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2)/tileHeight
;
473 int j
=(x
-(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2)/tileWidth
;
477 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
478 && j
< app
.board
.boardSize
[1]
479 && app
.board
.board
[i
][j
] != 0) {
480 selection1
.set(i
, j
);
481 paint(StatePaint
.SELECTED1
);
482 control(StatePlay
.SELECTED1
);
486 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
487 && j
< app
.board
.boardSize
[1]
488 && app
.board
.board
[i
][j
] != 0) {
489 if (selection1
.equals(i
, j
)) {
490 paint(StatePaint
.BOARD
);
491 control(StatePlay
.IDLE
);
493 selection2
.set(i
, j
);
494 paint(StatePaint
.SELECTED2
);
496 Point a
= selection1
.copy();
497 Point b
= selection2
.copy();
498 path
= app
.board
.getPath(a
, b
);
499 paint(StatePaint
.MATCHED
);
501 paint(StatePaint
.BOARD
);
502 if (path
.size() > 0) {
503 app
.board
.play(a
, b
);
506 paint(StatePaint
.BOARD
);
508 pairs
= app
.board
.getPairs(1);
509 if (pairs
.size() == 0) {
510 if (app
.board
.getNumPieces() == 0) {
511 paint(StatePaint
.WIN
);
514 paint(StatePaint
.LOSE
);
516 control(StatePlay
.GAMEOVER
);
518 control(StatePlay
.IDLE
);
520 //undo.sensitive=app.board.getCanUndo();
526 paint(StatePaint
.BOARD
);
529 } catch (Exception e
) {
534 private void checkforhiscore() {
535 if (timerRegistered
) {
538 final String
[] sizes
= { "S", "M", "L" };
539 final String
[] diffs
= { "E", "H" };
540 String prefname1
= "hiscore_" + diffs
[app
.difficulty
-1] + sizes
[app
.size
-1] + "1";
541 String prefname2
= "hiscore_" + diffs
[app
.difficulty
-1] + sizes
[app
.size
-1] + "2";
542 // get hiscores for current size/difficulty
543 SharedPreferences sp
= PreferenceManager
.getDefaultSharedPreferences(app
);
544 String besttime1
= sp
.getString(prefname1
, INVALID_TIME
);
545 String besttime2
= sp
.getString(prefname2
, INVALID_TIME
);
546 // did we win something?
547 if (time
.compareTo(besttime2
) < 0) {
549 new AlertDialog
.Builder(app
.activity
)
550 .setTitle("Hiscore!")
552 .setIcon(R
.drawable
.icon
)
553 .setPositiveButton(app
.getString(android
.R
.string
.ok
), null)
554 .setMessage("You've made the highscore list!").create() // FIXME: hardcoded string
557 SharedPreferences
.Editor editor
= sp
.edit();
558 if (time
.compareTo(besttime1
) < 0) {
559 editor
.putString(prefname1
, time
);
560 editor
.putString(prefname2
, besttime1
);
562 editor
.putString(prefname2
, time
);
568 public void surfaceChanged(SurfaceHolder holder
, int format
, int width
,
570 surfaceHolder
= holder
;
571 if (cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
577 public void surfaceCreated(SurfaceHolder holder
) {
578 surfaceHolder
= holder
;
582 public void surfaceDestroyed(SurfaceHolder holder
) {
583 surfaceHolder
= null;
584 if (timerRegistered
) {