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
;
46 private Point selection1
= new Point(0, 0);
47 private Point selection2
= new Point(0, 0);
48 private List
<Point
> path
= null;
49 private List
<Line
> pairs
= null;
50 private long startTime
;
51 private long playTime
;
52 private long baseTime
;
54 private Tileset tileset
;
56 static class hHandler
extends Handler
{
57 private final WeakReference
<ShisenShoView
> mTarget
;
59 hHandler(ShisenShoView target
) {
60 mTarget
= new WeakReference
<ShisenShoView
>(target
);
64 public void handleMessage(Message msg
) {
65 ShisenShoView target
= mTarget
.get();
67 target
.onUpdateTime();
71 private Handler timerHandler
= new hHandler(this);
73 private boolean timerRegistered
= false;
74 private ShisenSho app
;
75 private StatePlay cstate
;
76 private StatePaint pstate
;
77 private Canvas canvas
= null;
78 private SurfaceHolder surfaceHolder
= null;
79 private String time
= INVALID_TIME
;
81 public ShisenShoView(ShisenSho shisenSho
) {
82 super((Context
) shisenSho
);
84 cstate
= StatePlay
.UNINITIALIZED
;
85 surfaceHolder
= getHolder();
86 surfaceHolder
.addCallback(this);
87 tileset
= new Tileset(shisenSho
);
90 public ShisenShoView(Context ctx
) {
92 this.app
= (ShisenSho
) ctx
;
93 cstate
= StatePlay
.UNINITIALIZED
;
94 surfaceHolder
= getHolder();
95 surfaceHolder
.addCallback(this);
98 private void paint(StatePaint pstate
) {
103 private void control(StatePlay cstate
) {
107 private void loadBackground() {
108 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
109 ops
.inScaled
= false;
110 bg
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.kshisen_bgnd
, ops
);
111 bg
.setDensity(Bitmap
.DENSITY_NONE
);
114 private void registerTimer() {
116 return; // Already registered
118 timer
.scheduleAtFixedRate(new TimerTask() {
120 timerHandler
.sendEmptyMessage(Activity
.RESULT_OK
);
123 timerRegistered
= true;
126 private void unregisterTimer() {
128 return; // Already unregistered
131 timerRegistered
= false;
134 public void pauseTime() {
137 startTime
= System
.currentTimeMillis();
141 public void resumeTime() {
142 startTime
= System
.currentTimeMillis();
146 private void updateTime() {
147 if (cstate
!=StatePlay
.GAMEOVER
) {
148 playTime
= (System
.currentTimeMillis()-startTime
)/1000+baseTime
;
152 public void loadTileset() {
153 tileset
.loadTileset(screenWidth
, screenHeight
);
156 private void initializeGame() {
158 screenWidth
=getWidth();
159 screenHeight
=getHeight();
161 //undo.sensitive=false;
162 pstate
=StatePaint
.BOARD
;
164 control(StatePlay
.IDLE
);
165 startTime
=System
.currentTimeMillis();
168 if (!timerRegistered
) {
171 pairs
=app
.board
.getPairs(1);
174 public boolean onOptionsItemSelected(MenuItem item
) {
175 // Handle item selection
176 switch (item
.getItemId()) {
178 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
181 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
184 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
195 public void reset() {
196 control(StatePlay
.UNINITIALIZED
);
197 paint(StatePaint
.BOARD
);
200 private void onHintActivate() {
201 if (cstate
!=StatePlay
.GAMEOVER
) {
202 pairs
=app
.board
.getPairs(1);
203 paint(StatePaint
.HINT
);
205 paint(StatePaint
.BOARD
);
206 control(StatePlay
.IDLE
);
210 private void onUndoActivate() {
211 if (app
.board
.getCanUndo()) {
212 if (cstate
==StatePlay
.GAMEOVER
&& !timerRegistered
) {
213 // Reprogram the time update that had been
214 // deactivated with the game over status
218 paint(StatePaint
.BOARD
);
219 //undo.sensitive=app.board.getCanUndo();
220 control(StatePlay
.IDLE
);
224 public void onTimeCounterActivate() {
225 if (cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
226 // Reprogram the time update that had been
227 // deactivated with the time_counter=false
232 private void onUpdateTime() {
234 if (cstate
==StatePlay
.GAMEOVER
) {
239 @SuppressWarnings("deprecation")
240 public static void drawMessage(Canvas canvas
, int x
, int y
,
241 boolean centered
, String message
, float textSize
) {
242 Paint paint
= new Paint();
243 paint
.setLinearText(true);
244 paint
.setAntiAlias(true);
245 paint
.setTextAlign(centered ? Align
.CENTER
: Align
.LEFT
);
246 paint
.setTypeface(Typeface
.SANS_SERIF
);
247 paint
.setFakeBoldText(true);
248 paint
.setTextSize(textSize
);
249 paint
.setColor(Color
.parseColor(COLOR_TEXT_SHADOW
));
250 canvas
.drawText(message
, x
+ 1, y
+ 1, paint
);
251 paint
.setColor(Color
.parseColor(COLOR_TEXT
));
252 canvas
.drawText(message
, x
, y
, paint
);
255 public void repaint() {
256 if (surfaceHolder
== null) return;
258 if (canvas
== null) canvas
= surfaceHolder
.lockCanvas(null);
259 if (canvas
== null) return;
260 if (cstate
==StatePlay
.UNINITIALIZED
) initializeGame();
261 synchronized (surfaceHolder
) {
265 if (canvas
!= null) {
266 surfaceHolder
.unlockCanvasAndPost(canvas
);
272 protected void doDraw(Canvas canvas
) {
274 if (canvas
== null) return;
276 // Board upper left corner on screen
280 if (app
!=null && app
.board
!=null) {
281 x0
=(screenWidth
-app
.board
.boardSize
[1]*tileset
.tileWidth
)/2;
282 y0
=(screenHeight
-app
.board
.boardSize
[0]*tileset
.tileHeight
)/2;
285 int selectcolor
= Color
.parseColor(COLOR_SELECTED
);
286 int hintcolor
= Color
.parseColor(COLOR_HINT
);
288 // Background & board painting
298 // Background painting
299 int bgWidth
= bg
.getWidth();
300 int bgHeight
= bg
.getHeight();
301 for (int i
=0; i
<screenHeight
/bgHeight
+1; i
++) {
302 for (int j
=0; j
<screenWidth
/bgWidth
+1; j
++) {
303 canvas
.drawBitmap(bg
, j
*bgWidth
, i
*bgHeight
, null);
308 // Max visible size: 7x17
309 if (app
!=null && app
.board
!=null) {
310 for (int i
=0;i
<app
.board
.boardSize
[0];i
++) {
311 for (int j
=0;j
<app
.board
.boardSize
[1];j
++) {
312 // Tiles are 56px height, 40px width each
313 char piece
=app
.board
.board
[i
][j
];
317 x0
+j
*tileset
.tileWidth
,
318 y0
+i
*tileset
.tileHeight
,
327 // rectangle for selection 1
332 highlightTile(canvas
, x0
, y0
, selection1
, selectcolor
);
336 // rectangle for selection 2
340 highlightTile(canvas
, x0
, y0
, selection2
, selectcolor
);
349 for (Point p1
: path
) {
351 drawLine(canvas
, x0
, y0
, p0
, p1
, selectcolor
);
362 if (pairs
!= null && pairs
.size() > 0) {
363 Line pair
= pairs
.get(0);
366 path
= app
.board
.getPath(a
, b
);
368 highlightTile(canvas
, x0
, y0
, a
, hintcolor
);
372 for (Point p1
: path
) {
374 drawLine(canvas
, x0
, y0
, p0
, p1
, hintcolor
);
381 highlightTile(canvas
, x0
, y0
, b
, hintcolor
);
386 // Win & loose notifications
389 drawMessage(canvas
, screenWidth
/ 2, screenHeight
/ 2, true,
393 drawMessage(canvas
, screenWidth
/ 2, screenHeight
/ 2, true,
408 int hours
= (int) (playTime
/ (60 * 60));
409 int minutes
= (int) ((playTime
/ 60) % 60);
410 int seconds
= (int) (playTime
% 60);
412 time
= String
.format(Locale
.US
, "%01d:%02d:%02d",
413 hours
, minutes
, seconds
);
418 int timePosX
=screenWidth
-120;
419 int timePosY
=screenHeight
-10;
421 if (app
.timeCounter
) {
422 drawMessage(canvas
, timePosX
, timePosY
, false, time
, 30);
427 } catch (Exception e
) {
433 private void drawLine(Canvas canvas
, int x0
, int y0
, Point p0
, Point p1
, int color
) {
434 Paint paint
= new Paint();
435 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
436 paint
.setColor(color
);
437 paint
.setStyle(Style
.STROKE
);
438 paint
.setStrokeCap(Cap
.ROUND
);
439 paint
.setStrokeJoin(Join
.ROUND
);
440 paint
.setStrokeWidth(3);
442 x0
+ p0
.j
* tileset
.tileWidth
- 2 + (tileset
.tileWidth
/ 2),
443 y0
+ p0
.i
* tileset
.tileHeight
- 2 + (tileset
.tileHeight
/ 2),
444 x0
+ p1
.j
* tileset
.tileWidth
- 2 + (tileset
.tileWidth
/ 2),
445 y0
+ p1
.i
* tileset
.tileHeight
- 2 + (tileset
.tileHeight
/ 2), paint
);
448 private void highlightTile(Canvas canvas
, int x0
, int y0
, Point p
, int color
) {
449 Paint paint
= new Paint();
450 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
451 paint
.setColor(color
);
452 paint
.setStyle(Style
.STROKE
);
453 paint
.setStrokeCap(Cap
.ROUND
);
454 paint
.setStrokeJoin(Join
.ROUND
);
455 paint
.setStrokeWidth(3);
457 x0
+ p
.j
* tileset
.tileWidth
- 2,
458 y0
+ p
.i
* tileset
.tileHeight
- 2,
459 x0
+ p
.j
* tileset
.tileWidth
+ tileset
.tileWidth
+ 2,
460 y0
+ p
.i
* tileset
.tileHeight
+ tileset
.tileHeight
+ 2);
461 canvas
.drawRect(r
, paint
);
465 public boolean onTouchEvent(MotionEvent event
) {
466 if (event
.getAction()==MotionEvent
.ACTION_DOWN
) {
467 onClick(Math
.round(event
.getX()),Math
.round(event
.getY()));
469 return super.onTouchEvent(event
);
472 private void onClick(int x
, int y
) {
474 int i
=(y
-(screenHeight
-app
.board
.boardSize
[0]*tileset
.tileHeight
)/2)/tileset
.tileHeight
;
475 int j
=(x
-(screenWidth
-app
.board
.boardSize
[1]*tileset
.tileWidth
)/2)/tileset
.tileWidth
;
479 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
480 && j
< app
.board
.boardSize
[1]
481 && app
.board
.board
[i
][j
] != 0) {
482 selection1
.set(i
, j
);
483 paint(StatePaint
.SELECTED1
);
484 control(StatePlay
.SELECTED1
);
488 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
489 && j
< app
.board
.boardSize
[1]
490 && app
.board
.board
[i
][j
] != 0) {
491 if (selection1
.equals(i
, j
)) {
492 paint(StatePaint
.BOARD
);
493 control(StatePlay
.IDLE
);
495 selection2
.set(i
, j
);
496 paint(StatePaint
.SELECTED2
);
498 Point a
= selection1
.copy();
499 Point b
= selection2
.copy();
500 path
= app
.board
.getPath(a
, b
);
501 paint(StatePaint
.MATCHED
);
503 paint(StatePaint
.BOARD
);
504 if (path
.size() > 0) {
505 app
.board
.play(a
, b
);
508 paint(StatePaint
.BOARD
);
510 pairs
= app
.board
.getPairs(1);
511 if (pairs
.size() == 0) {
512 if (app
.board
.getNumPieces() == 0) {
513 paint(StatePaint
.WIN
);
516 paint(StatePaint
.LOSE
);
518 control(StatePlay
.GAMEOVER
);
520 control(StatePlay
.IDLE
);
522 //undo.sensitive=app.board.getCanUndo();
528 paint(StatePaint
.BOARD
);
531 } catch (Exception e
) {
536 private void checkforhiscore() {
537 if (timerRegistered
) {
540 final String
[] sizes
= { "S", "M", "L" };
541 final String
[] diffs
= { "E", "H" };
542 String prefname1
= "hiscore_" + diffs
[app
.difficulty
-1] + sizes
[app
.size
-1] + "1";
543 String prefname2
= "hiscore_" + diffs
[app
.difficulty
-1] + sizes
[app
.size
-1] + "2";
544 // get hiscores for current size/difficulty
545 SharedPreferences sp
= PreferenceManager
.getDefaultSharedPreferences(app
);
546 String besttime1
= sp
.getString(prefname1
, INVALID_TIME
);
547 String besttime2
= sp
.getString(prefname2
, INVALID_TIME
);
548 // did we win something?
549 if (time
.compareTo(besttime2
) < 0) {
551 new AlertDialog
.Builder(app
.activity
)
552 .setTitle("Hiscore!")
554 .setIcon(R
.drawable
.icon
)
555 .setPositiveButton(app
.getString(android
.R
.string
.ok
), null)
556 .setMessage("You've made the highscore list!").create() // FIXME: hardcoded string
559 SharedPreferences
.Editor editor
= sp
.edit();
560 if (time
.compareTo(besttime1
) < 0) {
561 editor
.putString(prefname1
, time
);
562 editor
.putString(prefname2
, besttime1
);
564 editor
.putString(prefname2
, time
);
570 public void surfaceChanged(SurfaceHolder holder
, int format
, int width
,
572 surfaceHolder
= holder
;
573 if (cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
579 public void surfaceCreated(SurfaceHolder holder
) {
580 surfaceHolder
= holder
;
584 public void surfaceDestroyed(SurfaceHolder holder
) {
585 surfaceHolder
= null;
586 if (timerRegistered
) {