diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java index fbdeb2160..391e029a6 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -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 ) // { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java index 9eb6b62b4..5e38239a2 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java @@ -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 diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWConstants.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWConstants.java new file mode 100644 index 000000000..1f61f4e5e --- /dev/null +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWConstants.java @@ -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"; +} diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/BoardHandler.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/BoardHandler.java index c7c35dd04..1366dfb49 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/BoardHandler.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/BoardHandler.java @@ -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 ); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java new file mode 100644 index 000000000..59bc98156 --- /dev/null +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java @@ -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 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(); + } + + 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 ); + } +} diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XW_UtilCtxt.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XW_UtilCtxt.java index 8a3776b14..6b5807677 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XW_UtilCtxt.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XW_UtilCtxt.java @@ -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 );