mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-29 08:34:37 +01:00
All I wanted was to implement a tile picker, but it turns out dialogs
can't be synchronous on Android: you can't block the main UI thread. So now there's a new background thread to which all jni activity except game opening and saving is delegated (and these happen before and after the thread exists.) Tile picking works because the request comes in on the background thread which then blocks until the BoardActivity reports that the tile picker's gone away.
This commit is contained in:
parent
50991fd028
commit
2c3e45318d
6 changed files with 287 additions and 56 deletions
|
@ -11,15 +11,19 @@ import android.view.MenuInflater;
|
|||
import android.content.res.AssetManager;
|
||||
import java.io.InputStream;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.Intent;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import org.eehouse.android.xw4.jni.*;
|
||||
|
||||
public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
|
||||
|
||||
private static final String CUR_GAME = "cur_game";
|
||||
private static final int PICK_TILE_REQUEST = 1;
|
||||
|
||||
private BoardView m_view;
|
||||
private int m_jniGamePtr;
|
||||
|
@ -28,6 +32,13 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
|
|||
private Handler m_handler;
|
||||
private TimerRunnable[] m_timers;
|
||||
|
||||
// call startActivityForResult synchronously
|
||||
private Semaphore m_forResultWait = new Semaphore(0);
|
||||
private int m_resultCode = 0;
|
||||
private Intent m_resultIntent = null;
|
||||
|
||||
private JNIThread m_jniThread;
|
||||
|
||||
public class TimerRunnable implements Runnable {
|
||||
private int m_gamePtr;
|
||||
private int m_why;
|
||||
|
@ -40,7 +51,8 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
|
|||
}
|
||||
public void run() {
|
||||
m_timers[m_why] = null;
|
||||
XwJNI.timerFired( m_jniGamePtr, m_why, m_when, m_handle );
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_TIMER_FIRED,
|
||||
new Object[] { m_why, m_when, m_handle } );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,84 +100,87 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
|
|||
XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, this, m_view, 0,
|
||||
m_prefs, null, dictBytes );
|
||||
}
|
||||
m_view.startHandling( this, m_jniGamePtr, m_gi );
|
||||
|
||||
XwJNI.server_do( m_jniGamePtr );
|
||||
m_jniThread = new JNIThread( m_jniGamePtr,
|
||||
new Handler() {
|
||||
public void handleMessage( Message msg ) {
|
||||
Utils.logf( "handleMessage called" );
|
||||
m_view.invalidate();
|
||||
}
|
||||
} );
|
||||
m_jniThread.start();
|
||||
|
||||
m_view.startHandling( m_jniThread, m_jniGamePtr, m_gi );
|
||||
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_DO );
|
||||
|
||||
Utils.logf( "BoardActivity::onCreate() done" );
|
||||
} // onCreate
|
||||
|
||||
protected void onPause() {
|
||||
// save state here
|
||||
saveGame();
|
||||
super.onPause();
|
||||
}
|
||||
// protected void onPause() {
|
||||
// // save state here
|
||||
// saveGame();
|
||||
// super.onPause();
|
||||
// }
|
||||
|
||||
protected void onDestroy()
|
||||
{
|
||||
m_jniThread.waitToStop();
|
||||
saveGame();
|
||||
XwJNI.game_dispose( m_jniGamePtr );
|
||||
m_jniGamePtr = 0;
|
||||
super.onDestroy();
|
||||
Utils.logf( "onDestroy done" );
|
||||
}
|
||||
|
||||
|
||||
protected void onActivityResult( int requestCode, int resultCode,
|
||||
Intent result )
|
||||
{
|
||||
Utils.logf( "onActivityResult called" );
|
||||
this.m_resultCode = resultCode;
|
||||
this.m_resultIntent = result;
|
||||
this.m_forResultWait.release();
|
||||
}
|
||||
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate( R.menu.board_menu, menu );
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean toggleTray() {
|
||||
boolean draw;
|
||||
int state = XwJNI.board_getTrayVisState( m_jniGamePtr );
|
||||
if ( state == XwJNI.TRAY_REVEALED ) {
|
||||
draw = XwJNI.board_hideTray( m_jniGamePtr );
|
||||
} else {
|
||||
draw = XwJNI.board_showTray( m_jniGamePtr );
|
||||
}
|
||||
return draw;
|
||||
}
|
||||
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
boolean draw = false;
|
||||
boolean handled = true;
|
||||
|
||||
switch (item.getItemId()) {
|
||||
case R.id.board_menu_done:
|
||||
draw = XwJNI.board_commitTurn( m_jniGamePtr );
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_COMMIT );
|
||||
break;
|
||||
case R.id.board_menu_juggle:
|
||||
draw = XwJNI.board_juggleTray( m_jniGamePtr );
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_JUGGLE );
|
||||
break;
|
||||
case R.id.board_menu_flip:
|
||||
draw = XwJNI.board_flip( m_jniGamePtr );
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_FLIP );
|
||||
break;
|
||||
case R.id.board_menu_tray:
|
||||
draw = toggleTray();
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_TOGGLE_TRAY );
|
||||
break;
|
||||
|
||||
case R.id.board_menu_undo_current:
|
||||
draw = XwJNI.board_replaceTiles( m_jniGamePtr );
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_UNDO_CUR );
|
||||
break;
|
||||
case R.id.board_menu_undo_last:
|
||||
XwJNI.server_handleUndo( m_jniGamePtr );
|
||||
draw = true;
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_UNDO_LAST );
|
||||
break;
|
||||
|
||||
case R.id.board_menu_hint:
|
||||
XwJNI.board_resetEngine( m_jniGamePtr );
|
||||
// fallthru
|
||||
case R.id.board_menu_hint_next:
|
||||
draw = XwJNI.board_requestHint( m_jniGamePtr, false, null );
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_HINT );
|
||||
break;
|
||||
case R.id.board_menu_hint_next:
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_NEXT_HINT );
|
||||
break;
|
||||
|
||||
default:
|
||||
Utils.logf( "menuitem " + item.getItemId() + " not handled" );
|
||||
handled = false;
|
||||
}
|
||||
|
||||
if ( draw ) {
|
||||
m_view.invalidate();
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
|
@ -239,9 +254,7 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
|
|||
}
|
||||
|
||||
public void run() {
|
||||
if ( XwJNI.server_do( m_jniGamePtr ) ) {
|
||||
m_view.invalidate();
|
||||
}
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_DO );
|
||||
}
|
||||
|
||||
public void requestTime() {
|
||||
|
@ -267,6 +280,39 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
// This is supposed to be called from the jni thread
|
||||
public int userPickTile( int playerNum, String[] texts )
|
||||
{
|
||||
int tile = -1;
|
||||
Utils.logf( "util_userPickTile called" );
|
||||
|
||||
// Intent intent = new Intent( XWConstants.ACTION_PICK_TILE );
|
||||
// intent.setClassName( "org.eehouse.android.xw4",
|
||||
// "org.eehouse.android.xw4.TilePicker");
|
||||
Intent intent = new Intent( BoardActivity.this, TilePicker.class );
|
||||
intent.setAction( XWConstants.ACTION_PICK_TILE );
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putStringArray( XWConstants.PICK_TILE_TILES, texts );
|
||||
intent.putExtra( XWConstants.PICK_TILE_TILES, bundle );
|
||||
|
||||
try {
|
||||
startActivityForResult( intent, PICK_TILE_REQUEST );
|
||||
m_forResultWait.acquire();
|
||||
} catch ( Exception ee ) {
|
||||
Utils.logf( "userPickTile got: " + ee.toString() );
|
||||
}
|
||||
|
||||
if ( m_resultCode >= RESULT_FIRST_USER ) {
|
||||
tile = m_resultCode - RESULT_FIRST_USER;
|
||||
} else {
|
||||
Utils.logf( "unexpected result code: " + m_resultCode );
|
||||
}
|
||||
|
||||
Utils.logf( "util_userPickTile => " + tile );
|
||||
return tile;
|
||||
}
|
||||
|
||||
// Don't need this unless we have a scroll thumb to indicate position
|
||||
// public void yOffsetChange( int oldOffset, int newOffset )
|
||||
// {
|
||||
|
|
|
@ -31,6 +31,7 @@ public class BoardView extends View implements DrawCtx,
|
|||
private Drawable m_downArrow;
|
||||
private Drawable m_origin;
|
||||
private int m_top, m_left;
|
||||
private JNIThread m_jniThread;
|
||||
|
||||
private static final int BLACK = 0xFF000000;
|
||||
private static final int WHITE = 0xFFFFFFFF;
|
||||
|
@ -72,35 +73,28 @@ public class BoardView extends View implements DrawCtx,
|
|||
int action = event.getAction();
|
||||
int xx = (int)event.getX() - m_left;
|
||||
int yy = (int)event.getY() - m_top;
|
||||
boolean draw = false;
|
||||
|
||||
switch ( action ) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
boolean[] handled = new boolean[1];
|
||||
draw = XwJNI.board_handlePenDown( m_jniGamePtr, xx, yy, handled );
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_PEN_DOWN, xx, yy );
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
draw = XwJNI.board_handlePenMove( m_jniGamePtr, xx, yy );
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_PEN_MOVE, xx, yy );
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
draw = XwJNI.board_handlePenUp( m_jniGamePtr, xx, yy );
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_PEN_UP, xx, yy );
|
||||
break;
|
||||
default:
|
||||
Utils.logf( "unknown action: " + action );
|
||||
Utils.logf( event.toString() );
|
||||
}
|
||||
if ( draw ) {
|
||||
invalidate();
|
||||
}
|
||||
|
||||
return true; // required to get subsequent events
|
||||
}
|
||||
|
||||
protected void onDraw( Canvas canvas )
|
||||
{
|
||||
if ( layoutBoardOnce() ) {
|
||||
if ( !XwJNI.board_draw( m_jniGamePtr ) ) {
|
||||
Utils.logf( "draw not complete" );
|
||||
}
|
||||
canvas.drawBitmap( m_bitmap, m_left, m_top, new Paint() );
|
||||
}
|
||||
}
|
||||
|
@ -173,6 +167,7 @@ public class BoardView extends View implements DrawCtx,
|
|||
4 );
|
||||
|
||||
XwJNI.board_setShowColors( m_jniGamePtr, true ); // get from prefs!
|
||||
|
||||
XwJNI.board_invalAll( m_jniGamePtr );
|
||||
|
||||
m_bitmap = Bitmap.createBitmap( 1 + (cellSize*nCells),
|
||||
|
@ -180,15 +175,18 @@ public class BoardView extends View implements DrawCtx,
|
|||
+ (cellSize *(nCells-nToScroll)),
|
||||
Bitmap.Config.ARGB_8888 );
|
||||
m_canvas = new Canvas( m_bitmap );
|
||||
|
||||
// need to synchronize??
|
||||
m_jniThread.handle( JNIThread.JNICmd.CMD_DRAW );
|
||||
}
|
||||
return m_boardSet;
|
||||
}
|
||||
|
||||
public void startHandling( Context context, int gamePtr, CurGameInfo gi )
|
||||
public void startHandling( JNIThread thread, int gamePtr, CurGameInfo gi )
|
||||
{
|
||||
m_jniThread = thread;
|
||||
m_jniGamePtr = gamePtr;
|
||||
m_gi = gi;
|
||||
|
||||
}
|
||||
|
||||
// DrawCtxt interface implementation
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/* -*- compile-command: "cd ../../../../../; ant reinstall"; -*- */
|
||||
|
||||
|
||||
|
||||
package org.eehouse.android.xw4;
|
||||
|
||||
public interface XWConstants {
|
||||
public static final String PICK_TILE_TILES
|
||||
= "org.eehouse.android.xw4.PICK_TILE_TILES";
|
||||
public static final String PICK_TILE_TILE
|
||||
= "org.eehouse.android.xw4.PICK_TILE_TILE";
|
||||
|
||||
// These are duplicated in AndroidManifest.xml. If change here
|
||||
// must change there too to keep in sync.
|
||||
public final String ACTION_PICK_TILE
|
||||
= "org.eehouse.android.xw4.action.PICK_TILE";
|
||||
public final String CATEGORY_PICK_TILE
|
||||
= "org.eehouse.android.xw4.category.PICK_TILE";
|
||||
}
|
|
@ -5,6 +5,6 @@ import android.content.Context;
|
|||
|
||||
public interface BoardHandler {
|
||||
|
||||
void startHandling( Context context, int gamePtr, CurGameInfo gi );
|
||||
void startHandling( JNIThread thread, int gamePtr, CurGameInfo gi );
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
/* -*- compile-command: "cd ../../../../../../; ant reinstall"; -*- */
|
||||
|
||||
|
||||
package org.eehouse.android.xw4.jni;
|
||||
|
||||
import android.view.View;
|
||||
import org.eehouse.android.xw4.Utils;
|
||||
import java.lang.InterruptedException;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
|
||||
public class JNIThread extends Thread {
|
||||
|
||||
public enum JNICmd { CMD_NONE,
|
||||
CMD_DRAW,
|
||||
CMD_DO,
|
||||
CMD_PEN_DOWN,
|
||||
CMD_PEN_MOVE,
|
||||
CMD_PEN_UP,
|
||||
CMD_TIMER_FIRED,
|
||||
CMD_COMMIT,
|
||||
CMD_JUGGLE,
|
||||
CMD_FLIP,
|
||||
CMD_TOGGLE_TRAY,
|
||||
CMD_UNDO_CUR,
|
||||
CMD_UNDO_LAST,
|
||||
CMD_HINT,
|
||||
CMD_NEXT_HINT,
|
||||
};
|
||||
|
||||
private boolean m_stopped = false;
|
||||
private int m_jniGamePtr;
|
||||
private Handler m_handler;
|
||||
LinkedBlockingQueue<QueueElem> m_queue;
|
||||
|
||||
private class QueueElem {
|
||||
protected QueueElem( JNICmd cmd, Object[] args )
|
||||
{
|
||||
m_cmd = cmd; m_args = args;
|
||||
}
|
||||
JNICmd m_cmd;
|
||||
Object[] m_args;
|
||||
}
|
||||
|
||||
public JNIThread( int gamePtr, Handler handler ) {
|
||||
Utils.logf( "in JNIThread()" );
|
||||
m_jniGamePtr = gamePtr;
|
||||
m_handler = handler;
|
||||
|
||||
m_queue = new LinkedBlockingQueue<QueueElem>();
|
||||
}
|
||||
|
||||
public void waitToStop() {
|
||||
m_stopped = true;
|
||||
handle( JNICmd.CMD_NONE ); // tickle it
|
||||
try {
|
||||
join();
|
||||
} catch ( java.lang.InterruptedException ie ) {
|
||||
Utils.logf( "got InterruptedException: " + ie.toString() );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean toggleTray() {
|
||||
boolean draw;
|
||||
int state = XwJNI.board_getTrayVisState( m_jniGamePtr );
|
||||
if ( state == XwJNI.TRAY_REVEALED ) {
|
||||
draw = XwJNI.board_hideTray( m_jniGamePtr );
|
||||
} else {
|
||||
draw = XwJNI.board_showTray( m_jniGamePtr );
|
||||
}
|
||||
return draw;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
boolean[] handled = new boolean[1];
|
||||
while ( !m_stopped ) {
|
||||
QueueElem elem;
|
||||
Object[] args;
|
||||
try {
|
||||
elem = m_queue.take();
|
||||
} catch ( InterruptedException ie ) {
|
||||
Utils.logf( "interrupted; killing thread" );
|
||||
break;
|
||||
}
|
||||
boolean draw = false;
|
||||
args = elem.m_args;
|
||||
switch( elem.m_cmd ) {
|
||||
|
||||
case CMD_DRAW:
|
||||
draw = true;
|
||||
break;
|
||||
case CMD_DO:
|
||||
draw = XwJNI.server_do( m_jniGamePtr );
|
||||
break;
|
||||
|
||||
case CMD_PEN_DOWN:
|
||||
draw = XwJNI.board_handlePenDown( m_jniGamePtr,
|
||||
((Integer)args[0]).intValue(),
|
||||
((Integer)args[1]).intValue(),
|
||||
handled );
|
||||
break;
|
||||
case CMD_PEN_MOVE:
|
||||
draw = XwJNI.board_handlePenMove( m_jniGamePtr,
|
||||
((Integer)args[0]).intValue(),
|
||||
((Integer)args[1]).intValue() );
|
||||
break;
|
||||
case CMD_PEN_UP:
|
||||
draw = XwJNI.board_handlePenUp( m_jniGamePtr,
|
||||
((Integer)args[0]).intValue(),
|
||||
((Integer)args[1]).intValue() );
|
||||
break;
|
||||
|
||||
case CMD_COMMIT:
|
||||
draw = XwJNI.board_commitTurn( m_jniGamePtr );
|
||||
break;
|
||||
|
||||
case CMD_JUGGLE:
|
||||
draw = XwJNI.board_juggleTray( m_jniGamePtr );
|
||||
break;
|
||||
case CMD_FLIP:
|
||||
draw = XwJNI.board_flip( m_jniGamePtr );
|
||||
break;
|
||||
case CMD_TOGGLE_TRAY:
|
||||
draw = toggleTray();
|
||||
break;
|
||||
case CMD_UNDO_CUR:
|
||||
draw = XwJNI.board_replaceTiles( m_jniGamePtr );
|
||||
break;
|
||||
case CMD_UNDO_LAST:
|
||||
XwJNI.server_handleUndo( m_jniGamePtr );
|
||||
draw = true;
|
||||
break;
|
||||
|
||||
case CMD_HINT:
|
||||
XwJNI.board_resetEngine( m_jniGamePtr );
|
||||
// fallthru
|
||||
case CMD_NEXT_HINT:
|
||||
draw = XwJNI.board_requestHint( m_jniGamePtr, false, null );
|
||||
break;
|
||||
|
||||
case CMD_TIMER_FIRED:
|
||||
draw = XwJNI.timerFired( m_jniGamePtr,
|
||||
((Integer)args[0]).intValue(),
|
||||
((Integer)args[1]).intValue(),
|
||||
((Integer)args[2]).intValue() );
|
||||
break;
|
||||
}
|
||||
|
||||
if ( draw ) {
|
||||
if ( !XwJNI.board_draw( m_jniGamePtr ) ) {
|
||||
Utils.logf( "draw not complete" );
|
||||
}
|
||||
m_handler.post(null);
|
||||
}
|
||||
}
|
||||
Utils.logf( "run exiting" );
|
||||
} // run
|
||||
|
||||
public void handle( JNICmd cmd, Object... args )
|
||||
{
|
||||
QueueElem elem = new QueueElem( cmd, args );
|
||||
m_queue.add( elem );
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ public interface XW_UtilCtxt {
|
|||
static final int BONUS_TRIPLE_WORD = 4;
|
||||
|
||||
int getSquareBonus( int col, int row );
|
||||
int userPickTile( /* PickInfo* pi, add once tile-picking is enabled */
|
||||
int playerNum, String[] texts );
|
||||
|
||||
void setTimer( int why, int when, int handle );
|
||||
void clearTimer( int why );
|
||||
|
|
Loading…
Add table
Reference in a new issue