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
.Matrix
;
18 import android
.graphics
.Paint
;
19 import android
.graphics
.Paint
.Align
;
20 import android
.graphics
.Paint
.Cap
;
21 import android
.graphics
.Paint
.Join
;
22 import android
.graphics
.Paint
.Style
;
23 import android
.graphics
.Rect
;
24 import android
.graphics
.Typeface
;
25 import android
.os
.Handler
;
26 import android
.os
.Message
;
27 import android
.preference
.PreferenceManager
;
28 import android
.view
.MenuItem
;
29 import android
.view
.MotionEvent
;
30 import android
.view
.SurfaceHolder
;
31 import android
.view
.SurfaceView
;
33 class ShisenShoView
extends SurfaceView
implements SurfaceHolder
.Callback
{
35 private static final String INVALID_TIME
= "9:99:99";
36 private static final String COLOR_TEXT
= "#FFFFFF";
37 private static final String COLOR_TEXT_SHADOW
= "#000000";
38 private static final String COLOR_HINT
= "#F0C000";
39 private static final String COLOR_SELECTED
= "#FF0000";
41 private enum StatePlay
{ UNINITIALIZED
, IDLE
, SELECTED1
, SELECTED2
, GAMEOVER
};
42 private enum StatePaint
{ BOARD
, SELECTED1
, SELECTED2
, MATCHED
, WIN
, LOSE
, HINT
, TIME
};
44 private int screenWidth
;
45 private int screenHeight
;
46 private int tilesetRows
;
47 private int tilesetCols
;
48 private int tileHeight
;
49 private int tileWidth
;
51 private Bitmap tile
[];
52 private Point selection1
= new Point(0, 0);
53 private Point selection2
= new Point(0, 0);
54 private List
<Point
> path
= null;
55 private List
<Line
> pairs
= null;
56 private long startTime
;
57 private long playTime
;
58 private long baseTime
;
61 static class hHandler
extends Handler
{
62 private final WeakReference
<ShisenShoView
> mTarget
;
64 hHandler(ShisenShoView target
) {
65 mTarget
= new WeakReference
<ShisenShoView
>(target
);
69 public void handleMessage(Message msg
) {
70 ShisenShoView target
= mTarget
.get();
72 target
.onUpdateTime();
76 private Handler timerHandler
= new hHandler(this);
78 private boolean timerRegistered
= false;
79 private ShisenSho app
;
80 private StatePlay cstate
;
81 private StatePaint pstate
;
82 private Canvas canvas
= null;
83 private SurfaceHolder surfaceHolder
= null;
84 private String time
= INVALID_TIME
;
86 public ShisenShoView(ShisenSho shishenSho
) {
87 super((Context
) shishenSho
);
88 this.app
= shishenSho
;
89 cstate
= StatePlay
.UNINITIALIZED
;
90 surfaceHolder
= getHolder();
91 surfaceHolder
.addCallback(this);
94 public ShisenShoView(Context ctx
) {
96 this.app
= (ShisenSho
) ctx
;
97 cstate
= StatePlay
.UNINITIALIZED
;
98 surfaceHolder
= getHolder();
99 surfaceHolder
.addCallback(this);
102 private void paint(StatePaint pstate
) {
107 private void control(StatePlay cstate
) {
111 private void loadTileset() {
112 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
113 ops
.inScaled
= false;
114 Bitmap tileset
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.tileset
, ops
);
115 tileset
.setDensity(Bitmap
.DENSITY_NONE
);
117 // The tile set has 4 rows x 9 columns
120 int loadedtileWidth
= tileset
.getWidth()/tilesetCols
;
121 int loadedtileHeight
= tileset
.getHeight()/tilesetRows
;
122 tile
= new Bitmap
[tilesetRows
*tilesetCols
];
125 // "large" is 16x6, and we want to have a nice border, so we use 17x7 and
126 // choose the lowest scale so everything fits
127 float scalex
= ((float) screenWidth
/17) / loadedtileWidth
;
128 float scaley
= ((float) screenHeight
/7) / loadedtileHeight
;
129 if (scaley
< scalex
) {
134 Matrix matrix
= new Matrix();
135 matrix
.setScale(scalex
, scaley
);
138 for (int i
=0; i
<tilesetRows
; i
++) {
139 for (int j
=0; j
<tilesetCols
; j
++) {
140 tile
[k
] = Bitmap
.createBitmap(tileset
, j
*loadedtileWidth
, i
*loadedtileHeight
,
141 loadedtileWidth
, loadedtileHeight
, matrix
, false);
142 tile
[k
].setDensity(Bitmap
.DENSITY_NONE
);
146 tileWidth
= tile
[0].getWidth();
147 tileHeight
= tile
[0].getHeight();
150 private void loadBackground() {
151 BitmapFactory
.Options ops
= new BitmapFactory
.Options();
152 ops
.inScaled
= false;
153 bg
= BitmapFactory
.decodeResource(getResources(), R
.drawable
.kshisen_bgnd
, ops
);
154 bg
.setDensity(Bitmap
.DENSITY_NONE
);
157 private void registerTimer() {
159 return; // Already registered
161 timer
.scheduleAtFixedRate(new TimerTask() {
163 timerHandler
.sendEmptyMessage(Activity
.RESULT_OK
);
166 timerRegistered
= true;
169 private void unregisterTimer() {
171 return; // Already unregistered
174 timerRegistered
= false;
177 public void pauseTime() {
180 startTime
= System
.currentTimeMillis();
184 public void resumeTime() {
185 startTime
= System
.currentTimeMillis();
189 private void updateTime() {
190 if (cstate
!=StatePlay
.GAMEOVER
) {
191 playTime
= (System
.currentTimeMillis()-startTime
)/1000+baseTime
;
195 private void initializeGame() {
197 screenWidth
=getWidth();
198 screenHeight
=getHeight();
200 //undo.sensitive=false;
201 pstate
=StatePaint
.BOARD
;
203 control(StatePlay
.IDLE
);
204 startTime
=System
.currentTimeMillis();
207 if (!timerRegistered
) {
210 pairs
=app
.board
.getPairs(1);
213 public boolean onOptionsItemSelected(MenuItem item
) {
214 // Handle item selection
215 switch (item
.getItemId()) {
217 this.postDelayed(new Runnable() { public void run() { onHintActivate(); } }, 100);
220 this.postDelayed(new Runnable() { public void run() { onUndoActivate(); } }, 100);
223 this.postDelayed(new Runnable() { public void run() { reset(); } }, 100);
234 public void reset() {
235 control(StatePlay
.UNINITIALIZED
);
236 paint(StatePaint
.BOARD
);
239 private void onHintActivate() {
240 if (cstate
!=StatePlay
.GAMEOVER
) {
241 pairs
=app
.board
.getPairs(1);
242 paint(StatePaint
.HINT
);
244 paint(StatePaint
.BOARD
);
245 control(StatePlay
.IDLE
);
249 private void onUndoActivate() {
250 if (app
.board
.getCanUndo()) {
251 if (cstate
==StatePlay
.GAMEOVER
&& !timerRegistered
) {
252 // Reprogram the time update that had been
253 // deactivated with the game over status
257 paint(StatePaint
.BOARD
);
258 //undo.sensitive=app.board.getCanUndo();
259 control(StatePlay
.IDLE
);
263 public void onTimeCounterActivate() {
264 if (cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
265 // Reprogram the time update that had been
266 // deactivated with the time_counter=false
271 private void onUpdateTime() {
273 if (cstate
==StatePlay
.GAMEOVER
) {
278 @SuppressWarnings("deprecation")
279 public static void drawMessage(Canvas canvas
, int x
, int y
,
280 boolean centered
, String message
, float textSize
) {
281 Paint paint
= new Paint();
282 paint
.setLinearText(true);
283 paint
.setAntiAlias(true);
284 paint
.setTextAlign(centered ? Align
.CENTER
: Align
.LEFT
);
285 paint
.setTypeface(Typeface
.SANS_SERIF
);
286 paint
.setFakeBoldText(true);
287 paint
.setTextSize(textSize
);
288 paint
.setColor(Color
.parseColor(COLOR_TEXT_SHADOW
));
289 canvas
.drawText(message
, x
+ 1, y
+ 1, paint
);
290 paint
.setColor(Color
.parseColor(COLOR_TEXT
));
291 canvas
.drawText(message
, x
, y
, paint
);
294 public void repaint() {
295 if (surfaceHolder
== null) return;
297 if (canvas
== null) canvas
= surfaceHolder
.lockCanvas(null);
298 if (canvas
== null) return;
299 if (cstate
==StatePlay
.UNINITIALIZED
) initializeGame();
300 synchronized (surfaceHolder
) {
304 if (canvas
!= null) {
305 surfaceHolder
.unlockCanvasAndPost(canvas
);
311 protected void doDraw(Canvas canvas
) {
313 if (canvas
== null) return;
315 // Board upper left corner on screen
319 if (app
!=null && app
.board
!=null) {
320 x0
=(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2;
321 y0
=(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2;
324 int selectcolor
= Color
.parseColor(COLOR_SELECTED
);
325 int hintcolor
= Color
.parseColor(COLOR_HINT
);
327 // Background & board painting
337 // Background painting
338 int bgWidth
= bg
.getWidth();
339 int bgHeight
= bg
.getHeight();
340 for (int i
=0; i
<screenHeight
/bgHeight
+1; i
++) {
341 for (int j
=0; j
<screenWidth
/bgWidth
+1; j
++) {
342 canvas
.drawBitmap(bg
, j
*bgWidth
, i
*bgHeight
, null);
347 // Max visible size: 7x17
348 if (app
!=null && app
.board
!=null) {
349 for (int i
=0;i
<app
.board
.boardSize
[0];i
++) {
350 for (int j
=0;j
<app
.board
.boardSize
[1];j
++) {
351 // Tiles are 56px height, 40px width each
352 char piece
=app
.board
.board
[i
][j
];
354 canvas
.drawBitmap(tile
[piece
], x0
+j
*tileWidth
, y0
+i
*tileHeight
, null);
362 // rectangle for selection 1
367 highlightTile(canvas
, x0
, y0
, selection1
, selectcolor
);
371 // rectangle for selection 2
375 highlightTile(canvas
, x0
, y0
, selection2
, selectcolor
);
384 for (Point p1
: path
) {
386 drawLine(canvas
, x0
, y0
, p0
, p1
, selectcolor
);
397 if (pairs
!= null && pairs
.size() > 0) {
398 Line pair
= pairs
.get(0);
401 path
= app
.board
.getPath(a
, b
);
403 highlightTile(canvas
, x0
, y0
, a
, hintcolor
);
407 for (Point p1
: path
) {
409 drawLine(canvas
, x0
, y0
, p0
, p1
, hintcolor
);
416 highlightTile(canvas
, x0
, y0
, b
, hintcolor
);
421 // Win & loose notifications
424 drawMessage(canvas
, screenWidth
/ 2, screenHeight
/ 2, true,
428 drawMessage(canvas
, screenWidth
/ 2, screenHeight
/ 2, true,
443 int hours
= (int) (playTime
/ (60 * 60));
444 int minutes
= (int) ((playTime
/ 60) % 60);
445 int seconds
= (int) (playTime
% 60);
447 time
= String
.format(Locale
.US
, "%01d:%02d:%02d",
448 hours
, minutes
, seconds
);
453 int timePosX
=screenWidth
-120;
454 int timePosY
=screenHeight
-10;
456 if (app
.timeCounter
) {
457 drawMessage(canvas
, timePosX
, timePosY
, false, time
, 30);
462 } catch (Exception e
) {
468 private void drawLine(Canvas canvas
, int x0
, int y0
, Point p0
, Point p1
, int color
) {
469 Paint paint
= new Paint();
470 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
471 paint
.setColor(color
);
472 paint
.setStyle(Style
.STROKE
);
473 paint
.setStrokeCap(Cap
.ROUND
);
474 paint
.setStrokeJoin(Join
.ROUND
);
475 paint
.setStrokeWidth(3);
477 x0
+ p0
.j
* tileWidth
- 2 + (tileWidth
/ 2),
478 y0
+ p0
.i
* tileHeight
- 2 + (tileHeight
/ 2),
479 x0
+ p1
.j
* tileWidth
- 2 + (tileWidth
/ 2),
480 y0
+ p1
.i
* tileHeight
- 2 + (tileHeight
/ 2), paint
);
483 private void highlightTile(Canvas canvas
, int x0
, int y0
, Point p
, int color
) {
484 Paint paint
= new Paint();
485 paint
.setFlags(Paint
.ANTI_ALIAS_FLAG
);
486 paint
.setColor(color
);
487 paint
.setStyle(Style
.STROKE
);
488 paint
.setStrokeCap(Cap
.ROUND
);
489 paint
.setStrokeJoin(Join
.ROUND
);
490 paint
.setStrokeWidth(3);
492 x0
+ p
.j
* tileWidth
- 2,
493 y0
+ p
.i
* tileHeight
- 2,
494 x0
+ p
.j
* tileWidth
+ tileWidth
+ 2,
495 y0
+ p
.i
* tileHeight
+ tileHeight
+ 2);
496 canvas
.drawRect(r
, paint
);
500 public boolean onTouchEvent(MotionEvent event
) {
501 if (event
.getAction()==MotionEvent
.ACTION_DOWN
) {
502 onClick(Math
.round(event
.getX()),Math
.round(event
.getY()));
504 return super.onTouchEvent(event
);
507 private void onClick(int x
, int y
) {
509 int i
=(y
-(screenHeight
-app
.board
.boardSize
[0]*tileHeight
)/2)/tileHeight
;
510 int j
=(x
-(screenWidth
-app
.board
.boardSize
[1]*tileWidth
)/2)/tileWidth
;
514 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
515 && j
< app
.board
.boardSize
[1]
516 && app
.board
.board
[i
][j
] != 0) {
517 selection1
.set(i
, j
);
518 paint(StatePaint
.SELECTED1
);
519 control(StatePlay
.SELECTED1
);
523 if (i
>= 0 && i
< app
.board
.boardSize
[0] && j
>= 0
524 && j
< app
.board
.boardSize
[1]
525 && app
.board
.board
[i
][j
] != 0) {
526 if (selection1
.equals(i
, j
)) {
527 paint(StatePaint
.BOARD
);
528 control(StatePlay
.IDLE
);
530 selection2
.set(i
, j
);
531 paint(StatePaint
.SELECTED2
);
533 Point a
= selection1
.copy();
534 Point b
= selection2
.copy();
535 path
= app
.board
.getPath(a
, b
);
536 paint(StatePaint
.MATCHED
);
538 paint(StatePaint
.BOARD
);
539 if (path
.size() > 0) {
540 app
.board
.play(a
, b
);
543 paint(StatePaint
.BOARD
);
545 pairs
= app
.board
.getPairs(1);
546 if (pairs
.size() == 0) {
547 if (app
.board
.getNumPieces() == 0) {
548 paint(StatePaint
.WIN
);
551 paint(StatePaint
.LOSE
);
553 control(StatePlay
.GAMEOVER
);
555 control(StatePlay
.IDLE
);
557 //undo.sensitive=app.board.getCanUndo();
563 paint(StatePaint
.BOARD
);
566 } catch (Exception e
) {
571 private void checkforhiscore() {
572 if (timerRegistered
) {
575 final String
[] sizes
= { "S", "M", "L" };
576 final String
[] diffs
= { "E", "H" };
577 String prefname1
= "hiscore_" + diffs
[app
.difficulty
-1] + sizes
[app
.size
-1] + "1";
578 String prefname2
= "hiscore_" + diffs
[app
.difficulty
-1] + sizes
[app
.size
-1] + "2";
579 // get hiscores for current size/difficulty
580 SharedPreferences sp
= PreferenceManager
.getDefaultSharedPreferences(app
);
581 String besttime1
= sp
.getString(prefname1
, INVALID_TIME
);
582 String besttime2
= sp
.getString(prefname2
, INVALID_TIME
);
583 // did we win something?
584 if (time
.compareTo(besttime2
) < 0) {
586 new AlertDialog
.Builder(app
.activity
)
587 .setTitle("Hiscore!")
589 .setIcon(R
.drawable
.icon
)
590 .setPositiveButton(app
.getString(android
.R
.string
.ok
), null)
591 .setMessage("You've made the highscore list!").create() // FIXME: hardcoded string
594 SharedPreferences
.Editor editor
= sp
.edit();
595 if (time
.compareTo(besttime1
) < 0) {
596 editor
.putString(prefname1
, time
);
597 editor
.putString(prefname2
, besttime1
);
599 editor
.putString(prefname2
, time
);
605 public void surfaceChanged(SurfaceHolder holder
, int format
, int width
,
607 surfaceHolder
= holder
;
608 if (cstate
!=StatePlay
.GAMEOVER
&& !timerRegistered
) {
614 public void surfaceCreated(SurfaceHolder holder
) {
615 surfaceHolder
= holder
;
619 public void surfaceDestroyed(SurfaceHolder holder
) {
620 surfaceHolder
= null;
621 if (timerRegistered
) {