From 558d34bc98eb5ef618840bbe45d7075e3ad5bad2 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 25 Mar 2014 21:53:38 -0700 Subject: [PATCH] break part of BoardActivity out into BoardDelegate (and make a bunch of little changes that required) --- .../org/eehouse/android/xw4/BTService.java | 2 +- .../eehouse/android/xw4/BoardActivity.java | 2141 +--------------- .../eehouse/android/xw4/BoardDelegate.java | 2273 +++++++++++++++++ .../org/eehouse/android/xw4/BoardView.java | 5 +- .../org/eehouse/android/xw4/DelegateBase.java | 51 +- .../org/eehouse/android/xw4/DlgDelegate.java | 7 + .../org/eehouse/android/xw4/RelayService.java | 4 +- .../org/eehouse/android/xw4/SMSService.java | 2 +- .../src/org/eehouse/android/xw4/Toolbar.java | 11 +- .../org/eehouse/android/xw4/XWActivity.java | 3 +- .../eehouse/android/xw4/jni/BoardHandler.java | 4 +- 11 files changed, 2363 insertions(+), 2140 deletions(-) create mode 100644 xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java index db9949cf4..dedcc03c0 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java @@ -481,7 +481,7 @@ public class BTService extends XWService { host.getAddress() ); for ( long rowid : rowids ) { - if ( BoardActivity.feedMessage( gameID, buffer, addr ) ) { + if ( BoardDelegate.feedMessage( gameID, buffer, addr ) ) { // do nothing } else if ( haveGame && GameUtils.feedMessage( BTService.this, rowid, 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 17ddebb29..6ba2dc2c2 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -64,11 +64,7 @@ import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; import org.eehouse.android.xw4.jni.JNIThread.*; -public class BoardActivity extends XWActivity - implements TransportProcs.TPMsgHandler, View.OnClickListener, - DictImportActivity.DownloadFinishedListener, - ConnStatusHandler.ConnStatusCBacks, - NFCUtils.NFCActor { +public class BoardActivity extends Activity { public static final String INTENT_KEY_CHAT = "chat"; @@ -87,407 +83,14 @@ public class BoardActivity extends XWActivity private static final String WORDS = "WORDS"; private static final String GETDICT = "GETDICT"; - private BoardView m_view; - private int m_jniGamePtr; - private GameLock m_gameLock; - private CurGameInfo m_gi; - private GameSummary m_summary; - private CommsTransport m_xport; - private Handler m_handler = null; - private TimerRunnable[] m_timers; - private Runnable m_screenTimer; - private long m_rowid; - private Toolbar m_toolbar; - private View m_tradeButtons; - private Button m_exchCommmitButton; - private Button m_exchCancelButton; - - private ArrayList m_pendingChats; - - private String m_dlgBytes = null; - private EditText m_passwdEdit = null; - private LinearLayout m_passwdLyt = null; - private int m_dlgTitle; - private String m_dlgTitleStr; - private String[] m_texts; - private CommsConnType m_connType = CommsConnType.COMMS_CONN_NONE; - private String[] m_missingDevs; - private String m_curTiles; - private boolean m_canUndoTiles; - private boolean m_firingPrefs; - private JNIUtils m_jniu; - private boolean m_volKeysZoom; - private boolean m_inTrade; // save this in bundle? - private BoardUtilCtxt m_utils; - private int m_nMissingPlayers = -1; - private int m_invitesPending; - private boolean m_gameOver = false; - - // call startActivityForResult synchronously - private Semaphore m_forResultWait = new Semaphore(0); - private int m_resultCode; - - private Thread m_blockingThread; - private JNIThread m_jniThread; - private JNIThread.GameStateInfo m_gsi; - private DlgID m_blockingDlgID = DlgID.NONE; - - private String m_room; - private String m_toastStr; - private String[] m_words; - private String m_pwdName; - private String m_getDict; - - private int m_missing; - private boolean m_haveInvited = false; - private boolean m_overNotShown; - - private static HashSet s_this = new HashSet(); - - public static boolean feedMessage( int gameID, byte[] msg, - CommsAddrRec retAddr ) - { - boolean delivered = false; - int size; - synchronized( s_this ) { - size = s_this.size(); - if ( 1 == size ) { - BoardActivity self = s_this.iterator().next(); - Assert.assertNotNull( self.m_gi ); - Assert.assertNotNull( self.m_gameLock ); - Assert.assertNotNull( self.m_jniThread ); - if ( gameID == self.m_gi.gameID ) { - self.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, retAddr ); - delivered = true; - } - } - } - - if ( 1 < s_this.size() ) { - noteSkip(); - } - return delivered; - } - - public static boolean feedMessage( long rowid, byte[] msg ) - { - return feedMessages( rowid, new byte[][]{msg} ); - } - - public static boolean feedMessages( long rowid, byte[][] msgs ) - { - boolean delivered = false; - Assert.assertNotNull( msgs ); - int size; - synchronized( s_this ) { - size = s_this.size(); - if ( 1 == size ) { - BoardActivity self = s_this.iterator().next(); - Assert.assertNotNull( self.m_gi ); - Assert.assertNotNull( self.m_gameLock ); - Assert.assertNotNull( self.m_jniThread ); - if ( rowid == self.m_rowid ) { - delivered = true; // even if no messages! - for ( byte[] msg : msgs ) { - self.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, - null ); - } - } - } - } - if ( 1 < size ) { - noteSkip(); - } - return delivered; - } - - private static void setThis( BoardActivity self ) - { - synchronized( s_this ) { - Assert.assertTrue( !s_this.contains(self) ); // here - s_this.add( self ); - } - } - - private static void clearThis( BoardActivity self ) - { - synchronized( s_this ) { - Assert.assertTrue( s_this.contains( self ) ); - s_this.remove( self ); - } - } - - public class TimerRunnable implements Runnable { - private int m_why; - private int m_when; - private int m_handle; - private TimerRunnable( int why, int when, int handle ) { - m_why = why; - m_when = when; - m_handle = handle; - } - public void run() { - m_timers[m_why] = null; - if ( null != m_jniThread ) { - m_jniThread.handleBkgrnd( JNICmd.CMD_TIMER_FIRED, - m_why, m_when, m_handle ); - } - } - } + private BoardDelegate m_dlgt; @Override protected Dialog onCreateDialog( int id ) { Dialog dialog = super.onCreateDialog( id ); if ( null == dialog ) { - DialogInterface.OnClickListener lstnr; - AlertDialog.Builder ab; - - final DlgID dlgID = DlgID.values()[id]; - switch ( dlgID ) { - case DLG_OKONLY: - case DLG_RETRY: - case GAME_OVER: - case DLG_CONNSTAT: - ab = new AlertDialog.Builder( this ) - .setTitle( m_dlgTitle ) - .setMessage( m_dlgBytes ) - .setPositiveButton( R.string.button_ok, null ); - if ( DlgID.DLG_RETRY == dlgID ) { - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dlg, - int whichButton ) { - m_jniThread.handle( JNICmd.CMD_RESET ); - } - }; - ab.setNegativeButton( R.string.button_retry, lstnr ); - } else if ( XWApp.REMATCH_SUPPORTED && DlgID.GAME_OVER == dlgID ) { - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dlg, - int whichButton ) { - doRematch(); - } - }; - ab.setNegativeButton( R.string.button_rematch, lstnr ); - } else if ( DlgID.DLG_CONNSTAT == dlgID && - CommsConnType.COMMS_CONN_RELAY == m_connType ) { - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dlg, - int whichButton ) { - RelayService.reset( BoardActivity.this ); - } - }; - ab.setNegativeButton( R.string.button_reconnect, lstnr ); - } - dialog = ab.create(); - Utils.setRemoveOnDismiss( this, dialog, dlgID ); - break; - - case DLG_USEDICT: - case DLG_GETDICT: - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dlg, - int whichButton ) { - if ( DlgID.DLG_USEDICT == dlgID ) { - setGotGameDict( m_getDict ); - } else { - DictImportActivity - .downloadDictInBack( BoardActivity.this, - m_gi.dictLang, - m_getDict, - BoardActivity.this ); - } - } - }; - dialog = new AlertDialog.Builder( this ) - .setTitle( m_dlgTitle ) - .setMessage( m_dlgBytes ) - .setPositiveButton( R.string.button_yes, lstnr ) - .setNegativeButton( R.string.button_no, null ) - .create(); - Utils.setRemoveOnDismiss( this, dialog, dlgID ); - break; - - case DLG_DELETED: - ab = new AlertDialog.Builder( BoardActivity.this ) - .setTitle( R.string.query_title ) - .setMessage( R.string.msg_dev_deleted ) - .setPositiveButton( R.string.button_ok, null ); - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dlg, - int whichButton ) { - - waitCloseGame( false ); - GameUtils.deleteGame( BoardActivity.this, - m_rowid, false ); - finish(); - } - }; - ab.setNegativeButton( R.string.button_delete, lstnr ); - dialog = ab.create(); - break; - - case QUERY_REQUEST_BLK: - case QUERY_INFORM_BLK: - case DLG_SCORES: - case DLG_BADWORDS_BLK: - ab = new AlertDialog.Builder( this ) - .setMessage( m_dlgBytes ); - if ( 0 != m_dlgTitle ) { - ab.setTitle( m_dlgTitle ); - } - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dialog, - int whichButton ) { - m_resultCode = 1; - } - }; - ab.setPositiveButton( DlgID.QUERY_REQUEST_BLK == dlgID ? - R.string.button_yes : R.string.button_ok, - lstnr ); - if ( DlgID.QUERY_REQUEST_BLK == dlgID ) { - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dialog, - int whichButton ) { - m_resultCode = 0; - } - }; - ab.setNegativeButton( R.string.button_no, lstnr ); - } else if ( DlgID.DLG_SCORES == dlgID ) { - if ( null != m_words && m_words.length > 0 ) { - String buttonTxt; - boolean studyOn = XWPrefs.getStudyEnabled( this ); - if ( m_words.length == 1 ) { - int resID = studyOn - ? R.string.button_lookup_studyf - : R.string.button_lookupf; - buttonTxt = getString( resID, m_words[0] ); - } else { - int resID = studyOn ? R.string.button_lookup_study - : R.string.button_lookup; - buttonTxt = getString( resID ); - } - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dialog, - int whichButton ) { - showNotAgainDlgThen( R.string. - not_again_lookup, - R.string. - key_na_lookup, - Action.LOOKUP_ACTION ); - } - }; - ab.setNegativeButton( buttonTxt, lstnr ); - } - } - - dialog = ab.create(); - dialog.setOnDismissListener( makeODLforBlocking( id ) ); - break; - - case PICK_TILE_REQUESTBLANK_BLK: - case PICK_TILE_REQUESTTRAY_BLK: - ab = new AlertDialog.Builder( this ); - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dialog, - int item ) { - m_resultCode = item; - } - }; - ab.setItems( m_texts, lstnr ); - - if ( DlgID.PICK_TILE_REQUESTBLANK_BLK == dlgID ) { - ab.setTitle( R.string.title_tile_picker ); - } else { - ab.setTitle( getString( R.string.cur_tilesf, m_curTiles ) ); - if ( m_canUndoTiles ) { - DialogInterface.OnClickListener undoClicked = - new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dialog, - int whichButton ) { - m_resultCode = UtilCtxt.PICKER_BACKUP; - removeDialog( dlgID.ordinal() ); - } - }; - ab.setPositiveButton( R.string.tilepick_undo, - undoClicked ); - } - DialogInterface.OnClickListener doAllClicked = - new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dialog, - int whichButton ) { - m_resultCode = UtilCtxt.PICKER_PICKALL; - removeDialog( dlgID.ordinal() ); - } - }; - ab.setNegativeButton( R.string.tilepick_all, doAllClicked ); - } - - dialog = ab.create(); - dialog.setOnDismissListener( makeODLforBlocking( id ) ); - break; - - case ASK_PASSWORD_BLK: - if ( null == m_passwdLyt ) { - setupPasswdVars(); - } - m_passwdEdit.setText( "", TextView.BufferType.EDITABLE ); - ab = new AlertDialog.Builder( this ) - .setTitle( m_dlgTitleStr ) - .setView( m_passwdLyt ) - .setPositiveButton( R.string.button_ok, - new DialogInterface.OnClickListener() { - public void - onClick( DialogInterface dlg, - int whichButton ) { - m_resultCode = 1; - } - }); - dialog = ab.create(); - dialog.setOnDismissListener( makeODLforBlocking( id ) ); - break; - - case QUERY_ENDGAME: - dialog = new AlertDialog.Builder( this ) - .setTitle( R.string.query_title ) - .setMessage( R.string.ids_endnow ) - .setPositiveButton( R.string.button_yes, - new DialogInterface.OnClickListener() { - public void - onClick( DialogInterface dlg, - int item ) { - m_jniThread. - handle(JNICmd.CMD_ENDGAME); - } - }) - .setNegativeButton( R.string.button_no, null ) - .create(); - break; - case DLG_INVITE: - if ( null != m_room ) { - lstnr = new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dialog, - int item ) { - showInviteChoicesThen( Action.LAUNCH_INVITE_ACTION ); - } - }; - dialog = new AlertDialog.Builder( this ) - .setTitle( R.string.query_title ) - .setMessage( "" ) - .setPositiveButton( R.string.button_yes, lstnr ) - .setNegativeButton( R.string.button_no, null ) - .create(); - } - break; - - case ENABLE_NFC: - dialog = NFCUtils.makeEnableNFCDialog( this ); - break; - - default: - // just drop it; super.onCreateDialog likely failed - break; - } + dialog = m_dlgt.createDialog( id ); } return dialog; } // onCreateDialog @@ -495,67 +98,21 @@ public class BoardActivity extends XWActivity @Override public void onPrepareDialog( int id, Dialog dialog ) { - DbgUtils.logf( "BoardActivity:onPrepareDialog(id=%d)", id ); - DlgID dlgID = DlgID.values()[id]; - switch( dlgID ) { - case DLG_INVITE: - AlertDialog ad = (AlertDialog)dialog; - String message = getString( R.string.invite_msgf, m_missing ); - if ( m_missing > 1 ) { - message += getString( R.string.invite_multiple ); - } - ad.setMessage( message ); - break; - default: - super.onPrepareDialog( id, dialog ); - break; - } + super.onPrepareDialog( id, dialog ); + m_dlgt.prepareDialog( id, dialog ); } @Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate( savedInstanceState ); - getBundledData( savedInstanceState ); - - if ( CommonPrefs.getHideTitleBar( this ) - && ABUtils.haveMenuKey( this ) ) { - requestWindowFeature( Window.FEATURE_NO_TITLE ); - } - - if ( BuildConstants.CHAT_SUPPORTED ) { - m_pendingChats = new ArrayList(); - } - - m_utils = new BoardUtilCtxt(); - m_jniu = JNIUtilsImpl.get( this ); - setContentView( R.layout.board ); - m_timers = new TimerRunnable[4]; // needs to be in sync with - // XWTimerReason - m_view = (BoardView)findViewById( R.id.board_view ); - m_tradeButtons = findViewById( R.id.exchange_buttons ); - m_exchCommmitButton = setListenerOrHide( R.id.exchange_commit ); - m_exchCancelButton = setListenerOrHide( R.id.exchange_cancel ); - m_volKeysZoom = XWPrefs.getVolKeysZoom( this ); - - Intent intent = getIntent(); - m_rowid = intent.getLongExtra( GameUtils.INTENT_KEY_ROWID, -1 ); - DbgUtils.logf( "BoardActivity: opening rowid %d", m_rowid ); - m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false ); - m_overNotShown = true; - - NFCUtils.register( this ); // Don't seem to need to unregister... - - setBackgroundColor(); - setKeepScreenOn(); + m_dlgt = new BoardDelegate( this, savedInstanceState ); } // onCreate @Override protected void onPause() { - m_handler = null; - ConnStatusHandler.setHandler( null ); - waitCloseGame( true ); + m_dlgt.onPause(); super.onPause(); } @@ -563,124 +120,40 @@ public class BoardActivity extends XWActivity protected void onResume() { super.onResume(); - m_handler = new Handler(); - m_blockingDlgID = DlgID.NONE; - - setKeepScreenOn(); - - loadGame(); - - ConnStatusHandler.setHandler( this ); + m_dlgt.onResume(); } @Override protected void onSaveInstanceState( Bundle outState ) { super.onSaveInstanceState( outState ); - outState.putInt( DLG_TITLE, m_dlgTitle ); - outState.putString( DLG_TITLESTR, m_dlgTitleStr ); - outState.putString( DLG_BYTES, m_dlgBytes ); - outState.putString( ROOM, m_room ); - outState.putString( TOASTSTR, m_toastStr ); - outState.putStringArray( WORDS, m_words ); - outState.putString( PWDNAME, m_pwdName ); - outState.putString( GETDICT, m_getDict ); - } - - private void getBundledData( Bundle bundle ) - { - if ( null != bundle ) { - m_dlgTitleStr = bundle.getString( DLG_TITLESTR ); - m_dlgTitle = bundle.getInt( DLG_TITLE ); - m_dlgBytes = bundle.getString( DLG_BYTES ); - m_room = bundle.getString( ROOM ); - m_toastStr = bundle.getString( TOASTSTR ); - m_words = bundle.getStringArray( WORDS ); - m_pwdName = bundle.getString( PWDNAME ); - m_getDict = bundle.getString( GETDICT ); - } + m_dlgt.onSaveInstanceState( outState ); } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data ) { - if ( Activity.RESULT_CANCELED != resultCode ) { - switch ( requestCode ) { - case CHAT_REQUEST: - if ( BuildConstants.CHAT_SUPPORTED ) { - String msg = data.getStringExtra( INTENT_KEY_CHAT ); - if ( null != msg && msg.length() > 0 ) { - m_pendingChats.add( msg ); - trySendChats(); - } - } - break; - case BT_INVITE_RESULT: - // onActivityResult is called immediately *before* - // onResume -- meaning m_gi etc are still null. - m_missingDevs = data.getStringArrayExtra( BTInviteActivity.DEVS ); - break; - case SMS_INVITE_RESULT: - // onActivityResult is called immediately *before* - // onResume -- meaning m_gi etc are still null. - m_missingDevs = data.getStringArrayExtra( SMSInviteActivity.DEVS ); - break; - } - } + m_dlgt.onActivityResult( requestCode, resultCode, data ); } @Override public void onWindowFocusChanged( boolean hasFocus ) { super.onWindowFocusChanged( hasFocus ); - if ( hasFocus ) { - if ( m_firingPrefs ) { - m_firingPrefs = false; - m_volKeysZoom = XWPrefs.getVolKeysZoom( this ); - if ( null != m_jniThread ) { - m_jniThread.handle( JNICmd.CMD_PREFS_CHANGE ); - } - // in case of change... - setBackgroundColor(); - setKeepScreenOn(); - } - } + m_dlgt.onWindowFocusChanged( hasFocus ); } @Override public boolean onKeyDown( int keyCode, KeyEvent event ) { - boolean handled = false; - if ( null != m_jniThread ) { - XwJNI.XP_Key xpKey = keyCodeToXPKey( keyCode ); - if ( XwJNI.XP_Key.XP_KEY_NONE != xpKey ) { - m_jniThread.handle( JNICmd.CMD_KEYDOWN, xpKey ); - } else { - switch( keyCode ) { - case KeyEvent.KEYCODE_VOLUME_DOWN: - case KeyEvent.KEYCODE_VOLUME_UP: - if ( m_volKeysZoom ) { - int zoomBy = KeyEvent.KEYCODE_VOLUME_DOWN == keyCode - ? -2 : 2; - handled = doZoom( zoomBy ); - } - break; - } - } - } - return handled || super.onKeyDown( keyCode, event ); + return m_dlgt.onKeyDown( keyCode, event ) + || super.onKeyDown( keyCode, event ); } @Override public boolean onKeyUp( int keyCode, KeyEvent event ) { - if ( null != m_jniThread ) { - XwJNI.XP_Key xpKey = keyCodeToXPKey( keyCode ); - if ( XwJNI.XP_Key.XP_KEY_NONE != xpKey ) { - m_jniThread.handle( JNICmd.CMD_KEYUP, xpKey ); - } - } - return super.onKeyUp( keyCode, event ); + return m_dlgt.onKeyUp( keyCode, event ) || super.onKeyUp( keyCode, event ); } @Override @@ -695,1592 +168,14 @@ public class BoardActivity extends XWActivity @Override public boolean onPrepareOptionsMenu( Menu menu ) { - super.onPrepareOptionsMenu( menu ); - boolean inTrade = false; - MenuItem item; - int strId; - - if ( null != m_gsi ) { - inTrade = m_gsi.inTrade; - menu.setGroupVisible( R.id.group_done, !inTrade ); - menu.setGroupVisible( R.id.group_exchange, inTrade ); - - if ( UtilCtxt.TRAY_REVEALED == m_gsi.trayVisState ) { - strId = R.string.board_menu_tray_hide; - } else { - strId = R.string.board_menu_tray_show; - } - item = menu.findItem( R.id.board_menu_tray ); - item.setTitle( strId ); - - Utils.setItemVisible( menu, R.id.board_menu_flip, - m_gsi.visTileCount >= 1 ); - Utils.setItemVisible( menu, R.id.board_menu_toggle, - m_gsi.visTileCount >= 1 ); - Utils.setItemVisible( menu, R.id.board_menu_juggle, - m_gsi.canShuffle ); - Utils.setItemVisible( menu, R.id.board_menu_undo_current, - m_gsi.canRedo ); - Utils.setItemVisible( menu, R.id.board_menu_hint_prev, - m_gsi.canHint ); - Utils.setItemVisible( menu, R.id.board_menu_hint_next, - m_gsi.canHint ); - Utils.setItemVisible( menu, R.id.board_menu_chat, - BuildConstants.CHAT_SUPPORTED - && m_gsi.canChat ); - Utils.setItemVisible( menu, R.id.board_menu_tray, - !inTrade && m_gsi.canHideRack ); - Utils.setItemVisible( menu, R.id.board_menu_trade, - m_gsi.canTrade ); - Utils.setItemVisible( menu, R.id.board_menu_undo_last, - m_gsi.canUndo ); - } - - Utils.setItemVisible( menu, R.id.board_menu_invite, 0 < m_missing ); - - Utils.setItemVisible( menu, R.id.board_menu_trade_cancel, inTrade ); - Utils.setItemVisible( menu, R.id.board_menu_trade_commit, - inTrade && m_gsi.tradeTilesSelected - && m_gsi.curTurnSelected ); - Utils.setItemVisible( menu, R.id.board_menu_game_resign, !inTrade ); - - if ( !inTrade ) { - boolean enabled = null == m_gsi || m_gsi.curTurnSelected; - item = menu.findItem( R.id.board_menu_done ); - item.setVisible( enabled ); - if ( enabled ) { - if ( 0 >= m_view.curPending() ) { - strId = R.string.board_menu_pass; - } else { - strId = R.string.board_menu_done; - } - item.setTitle( strId ); - } - if ( m_gameOver || DBUtils.gameOver( this, m_rowid ) ) { - m_gameOver = true; - item = menu.findItem( R.id.board_menu_game_resign ); - item.setTitle( R.string.board_menu_game_final ); - } - } - - boolean enable = null != m_gi - && DeviceRole.SERVER_STANDALONE != m_gi.serverRole; - Utils.setItemVisible( menu, R.id.gamel_menu_checkmoves, enable ); - Utils.setItemVisible( menu, R.id.board_menu_game_resend, - enable && null != m_gsi && - 0 < m_gsi.nPendingMessages ); - - enable = enable && BuildConfig.DEBUG; - Utils.setItemVisible( menu, R.id.board_menu_game_netstats, enable ); - - enable = XWPrefs.getStudyEnabled( this ); - Utils.setItemVisible( menu, R.id.games_menu_study, enable ); - - return true; + return m_dlgt.onPrepareOptionsMenu( menu ) + || super.onPrepareOptionsMenu( menu ); } // onPrepareOptionsMenu public boolean onOptionsItemSelected( MenuItem item ) { - boolean handled = true; - JNICmd cmd = JNICmd.CMD_NONE; - Runnable proc = null; - - int id = item.getItemId(); - switch ( id ) { - case R.id.board_menu_done: - int nTiles = XwJNI.model_getNumTilesInTray( m_jniGamePtr, - m_view.getCurPlayer() ); - if ( XWApp.MAX_TRAY_TILES > nTiles ) { - showNotAgainDlgThen( R.string.not_again_done, - R.string.key_notagain_done, - Action.COMMIT_ACTION ); - } else { - dlgButtonClicked( Action.COMMIT_ACTION, AlertDialog.BUTTON_POSITIVE, null ); - } - break; - - case R.id.board_menu_trade_commit: - cmd = JNICmd.CMD_COMMIT; - break; - case R.id.board_menu_trade_cancel: - cmd = JNICmd.CMD_CANCELTRADE; - break; - - case R.id.board_menu_hint_prev: - cmd = JNICmd.CMD_PREV_HINT; - break; - case R.id.board_menu_hint_next: - cmd = JNICmd.CMD_NEXT_HINT; - break; - case R.id.board_menu_juggle: - cmd = JNICmd.CMD_JUGGLE; - break; - case R.id.board_menu_flip: - cmd = JNICmd.CMD_FLIP; - break; - case R.id.board_menu_zoom: - cmd = JNICmd.CMD_TOGGLEZOOM; - break; - case R.id.board_menu_chat: - startChatActivity(); - break; - case R.id.board_menu_toggle: - cmd = JNICmd.CMD_VALUES; - break; - - case R.id.board_menu_trade: - String msg = getString( R.string.not_again_trading ); - int strID = ABUtils.haveActionBar() ? R.string.not_again_trading_menu - : R.string. not_again_trading_buttons; - msg += getString( strID ); - showNotAgainDlgThen( msg, R.string.key_notagain_trading, - Action.START_TRADE_ACTION ); - break; - - case R.id.board_menu_tray: - cmd = JNICmd.CMD_TOGGLE_TRAY; - break; - case R.id.games_menu_study: - StudyListActivity.launchOrAlert( this, m_gi.dictLang, this ); - break; - case R.id.board_menu_game_netstats: - m_jniThread.handle( JNICmd.CMD_NETSTATS, R.string.netstats_title ); - break; - case R.id.board_menu_undo_current: - cmd = JNICmd.CMD_UNDO_CUR; - break; - case R.id.board_menu_undo_last: - showConfirmThen( R.string.confirm_undo_last, Action.UNDO_LAST_ACTION ); - break; - case R.id.board_menu_invite: - showDialog( DlgID.DLG_INVITE.ordinal() ); - break; - // small devices only - case R.id.board_menu_dict: - String dictName = m_gi.dictName( m_view.getCurPlayer() ); - DictBrowseActivity.launch( this, dictName ); - break; - - case R.id.board_menu_game_counts: - m_jniThread.handle( JNICmd.CMD_COUNTS_VALUES, - R.string.counts_values_title ); - break; - case R.id.board_menu_game_left: - m_jniThread.handle( JNICmd.CMD_REMAINING, - R.string.tiles_left_title ); - break; - - case R.id.board_menu_game_history: - m_jniThread.handle( JNICmd.CMD_HISTORY, R.string.history_title ); - break; - - case R.id.board_menu_game_resign: - m_jniThread.handle( JNICmd.CMD_FINAL, R.string.history_title ); - break; - - case R.id.board_menu_game_resend: - m_jniThread.handle( JNICmd.CMD_RESEND, true, false ); - break; - - case R.id.gamel_menu_checkmoves: - showNotAgainDlgThen( R.string.not_again_sync, - R.string.key_notagain_sync, - Action.SYNC_ACTION ); - break; - - case R.id.board_menu_file_prefs: - m_firingPrefs = true; - Utils.launchSettings( this ); - break; - - case R.id.board_menu_file_about: - showAboutDialog(); - break; - - default: - DbgUtils.logf( "menuitem %d not handled", id ); - handled = false; - } - - if ( handled && cmd != JNICmd.CMD_NONE ) { - m_jniThread.handle( cmd ); - } - return handled; - } - - ////////////////////////////////////////////////// - // DlgDelegate.DlgClickNotify interface - ////////////////////////////////////////////////// - @Override - public void dlgButtonClicked( Action action, int which, Object[] params ) - { - if ( Action.LAUNCH_INVITE_ACTION == action ) { - if ( DlgDelegate.DISMISS_BUTTON != which ) { - if ( DlgDelegate.NFC_BTN == which ) { - if ( NFCUtils.nfcAvail( this )[1] ) { - showNotAgainDlgThen( R.string.not_again_sms_ready, - R.string.key_notagain_sms_ready ); - } else { - showDialog( DlgID.ENABLE_NFC.ordinal() ); - } - } else { - String inviteID = GameUtils.formatGameID( m_gi.gameID ); - GameUtils.launchInviteActivity( this, which, m_room, - inviteID, m_gi.dictLang, - m_gi.dictName, - m_gi.nPlayers ); - } - } - } else if ( AlertDialog.BUTTON_POSITIVE == which ) { - JNICmd cmd = JNICmd.CMD_NONE; - switch ( action ) { - case UNDO_LAST_ACTION: - cmd = JNICmd.CMD_UNDO_LAST; - break; - case SYNC_ACTION: - doSyncMenuitem(); - break; - case BT_PICK_ACTION: - BTInviteActivity.launchForResult( this, m_nMissingPlayers, - BT_INVITE_RESULT ); - break; - case SMS_PICK_ACTION: - SMSInviteActivity.launchForResult( this, m_nMissingPlayers, - SMS_INVITE_RESULT ); - break; - case SMS_CONFIG_ACTION: - Utils.launchSettings( this ); - break; - - case COMMIT_ACTION: - cmd = JNICmd.CMD_COMMIT; - break; - case SHOW_EXPL_ACTION: - Utils.showToast( BoardActivity.this, m_toastStr ); - m_toastStr = null; - break; - case BUTTON_BROWSEALL_ACTION: - case BUTTON_BROWSE_ACTION: - String curDict = m_gi.dictName( m_view.getCurPlayer() ); - View button = m_toolbar.getViewFor( Toolbar.BUTTON_BROWSE_DICT ); - if ( Action.BUTTON_BROWSEALL_ACTION == action && - DictsActivity.handleDictsPopup( this, button, curDict ) ) { - break; - } - DictBrowseActivity.launch( this, curDict ); - break; - case PREV_HINT_ACTION: - cmd = JNICmd.CMD_PREV_HINT; - break; - case NEXT_HINT_ACTION: - cmd = JNICmd.CMD_NEXT_HINT; - break; - case JUGGLE_ACTION: - cmd = JNICmd.CMD_JUGGLE; - break; - case FLIP_ACTION: - cmd = JNICmd.CMD_FLIP; - break; - case ZOOM_ACTION: - cmd = JNICmd.CMD_TOGGLEZOOM; - break; - case UNDO_ACTION: - cmd = JNICmd.CMD_UNDO_CUR; - break; - case VALUES_ACTION: - cmd = JNICmd.CMD_VALUES; - break; - case CHAT_ACTION: - startChatActivity(); - break; - case START_TRADE_ACTION: - Utils.showToast( this, R.string.entering_trade ); - cmd = JNICmd.CMD_TRADE; - break; - case LOOKUP_ACTION: - launchLookup( m_words, m_gi.dictLang ); - break; - default: - Assert.fail(); - } - - if ( JNICmd.CMD_NONE != cmd ) { - checkAndHandle( cmd ); - } - } - } // dlgButtonClicked - - ////////////////////////////////////////////////// - // View.OnClickListener interface - ////////////////////////////////////////////////// - @Override - public void onClick( View view ) - { - if ( view == m_exchCommmitButton ) { - m_jniThread.handle( JNICmd.CMD_COMMIT ); - } else if ( view == m_exchCancelButton ) { - m_jniThread.handle( JNICmd.CMD_CANCELTRADE ); - } - } - - ////////////////////////////////////////////////// - // MultiService.MultiEventListener interface - ////////////////////////////////////////////////// - @Override - @SuppressWarnings("fallthrough") - public void eventOccurred( MultiService.MultiEvent event, final Object ... args ) - { - switch( event ) { - case MESSAGE_ACCEPTED: - case MESSAGE_REFUSED: - ConnStatusHandler. - updateStatusIn( this, this, CommsConnType.COMMS_CONN_BT, - MultiService.MultiEvent.MESSAGE_ACCEPTED == event); - break; - case MESSAGE_NOGAME: - int gameID = (Integer)args[0]; - if ( gameID == m_gi.gameID ) { - post( new Runnable() { - public void run() { - showDialog( DlgID.DLG_DELETED.ordinal() ); - } - } ); - } - break; - - // This can be BT or SMS. In BT case there's a progress - // thing going. Not in SMS case. - case NEWGAME_FAILURE: - DbgUtils.logf( "failed to create game" ); - case NEWGAME_SUCCESS: - final boolean success = - MultiService.MultiEvent.NEWGAME_SUCCESS == event; - final boolean allHere = 0 == --m_invitesPending; - m_handler.post( new Runnable() { - public void run() { - if ( allHere ) { - stopProgress(); - } - if ( success ) { - DbgUtils.showf( BoardActivity.this, - R.string.invite_success ); - } - } - } ); - break; - - case SMS_SEND_OK: - ConnStatusHandler.showSuccessOut( this ); - break; - case SMS_RECEIVE_OK: - ConnStatusHandler.showSuccessIn( this ); - break; - case SMS_SEND_FAILED: - case SMS_SEND_FAILED_NORADIO: - - // if ( null != m_jniThread ) { - // boolean accepted = - // MultiService.MultiEvent.SMS_RECEIVE_OK == event - // || MultiService.MultiEvent.SMS_SEND_OK == event; - // m_jniThread.handle( JNICmd.CMD_DRAW_SMS_STATUS, accepted ); - // } - break; - - default: - super.eventOccurred( event, args ); - break; - } - } - - ////////////////////////////////////////////////// - // TransportProcs.TPMsgHandler interface - ////////////////////////////////////////////////// - - public void tpmRelayConnd( final String room, final int devOrder, - final boolean allHere, final int nMissing ) - { - post( new Runnable() { - public void run() { - handleConndMessage( room, devOrder, allHere, nMissing ); - } - } ); - } - - public void tpmRelayErrorProc( TransportProcs.XWRELAY_ERROR relayErr ) - { - int strID = -1; - DlgID dlgID = DlgID.NONE; - boolean doToast = false; - - switch ( relayErr ) { - case TOO_MANY: - strID = R.string.msg_too_many; - dlgID = DlgID.DLG_OKONLY; - break; - case NO_ROOM: - strID = R.string.msg_no_room; - dlgID = DlgID.DLG_RETRY; - break; - case DUP_ROOM: - strID = R.string.msg_dup_room; - dlgID = DlgID.DLG_OKONLY; - break; - case LOST_OTHER: - case OTHER_DISCON: - strID = R.string.msg_lost_other; - doToast = true; - break; - - case DEADGAME: - case DELETED: - strID = R.string.msg_dev_deleted; - dlgID = DlgID.DLG_DELETED; - break; - - case OLDFLAGS: - case BADPROTO: - case RELAYBUSY: - case SHUTDOWN: - case TIMEOUT: - case HEART_YOU: - case HEART_OTHER: - break; - } - - if ( doToast ) { - Utils.showToast( this, strID ); - } else if ( dlgID != DlgID.NONE ) { - final int strIDf = strID; - final int dlgIDf = dlgID.ordinal(); - post( new Runnable() { - public void run() { - m_dlgBytes = getString( strIDf ); - m_dlgTitle = R.string.relay_alert; - showDialog( dlgIDf ); - } - }); - } - } - - ////////////////////////////////////////////////// - // DictImportActivity.DownloadFinishedListener interface - ////////////////////////////////////////////////// - public void downloadFinished( final String name, final boolean success ) - { - if ( success ) { - post( new Runnable() { - public void run() { - setGotGameDict( name ); - } - } ); - } - } - - ////////////////////////////////////////////////// - // NFCUtils.NFCActor - ////////////////////////////////////////////////// - - public String makeNFCMessage() - { - String data = null; - if ( 0 < m_missing ) { // Isn't there a better test?? - String inviteID = String.format( "%X", m_gi.gameID ); - String room = m_summary.roomName; - Assert.assertNotNull( room ); - data = NetLaunchInfo.makeLaunchJSON( this, room, inviteID, - m_gi.dictLang, - m_gi.dictName, m_gi.nPlayers ); - } - return data; - } - - ////////////////////////////////////////////////// - // ConnStatusHandler.ConnStatusCBacks - ////////////////////////////////////////////////// - public void invalidateParent() - { - runOnUiThread(new Runnable() { - public void run() { - m_view.invalidate(); - } - }); - } - - public void onStatusClicked() - { - final String msg = ConnStatusHandler.getStatusText( this, m_connType ); - post( new Runnable() { - public void run() { - m_dlgBytes = msg; - m_dlgTitle = R.string.info_title; - showDialog( DlgID.DLG_CONNSTAT.ordinal() ); - } - } ); - } - - public Handler getHandler() - { - return m_handler; - } - - private void setGotGameDict( String getDict ) - { - m_jniThread.setSaveDict( getDict ); - - String msg = getString( R.string.reload_new_dict, getDict ); - Utils.showToast( this, msg ); - finish(); - GameUtils.launchGame( this, m_rowid, false ); - } - - private XwJNI.XP_Key keyCodeToXPKey( int keyCode ) - { - XwJNI.XP_Key xpKey = XwJNI.XP_Key.XP_KEY_NONE; - switch( keyCode ) { - case KeyEvent.KEYCODE_DPAD_CENTER: - case KeyEvent.KEYCODE_ENTER: - xpKey = XwJNI.XP_Key.XP_RETURN_KEY; - break; - case KeyEvent.KEYCODE_DPAD_DOWN: - xpKey = XwJNI.XP_Key.XP_CURSOR_KEY_DOWN; - break; - case KeyEvent.KEYCODE_DPAD_LEFT: - xpKey = XwJNI.XP_Key.XP_CURSOR_KEY_LEFT; - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - xpKey = XwJNI.XP_Key.XP_CURSOR_KEY_RIGHT; - break; - case KeyEvent.KEYCODE_DPAD_UP: - xpKey = XwJNI.XP_Key.XP_CURSOR_KEY_UP; - break; - case KeyEvent.KEYCODE_SPACE: - xpKey = XwJNI.XP_Key.XP_RAISEFOCUS_KEY; - break; - } - return xpKey; - } - - // Blocking thread stuff: The problem this is solving occurs when - // you have a blocking dialog up, meaning the jni thread is - // blocked, and you hit the home button. onPause() gets called - // which wants to use jni calls to e.g. summarize. For those to - // succeed (the jni being non-reentrant and set up to assert if it - // is reentered) the jni thread must first be unblocked and - // allowed to return back through the jni. We unblock using - // Thread.interrupt method, the exception from which winds up - // caught in waitBlockingDialog. The catch dismisses the dialog - // with the default/cancel value, but that takes us into the - // onDismissListener which normally releases the semaphore. But - // if we've interrupted then we can't release it or blocking won't - // work for as long as this activity lives. Hence - // releaseIfBlocking(). This feels really fragile but it does - // work. - private void setBlockingThread() - { - synchronized( this ) { - Assert.assertTrue( null == m_blockingThread ); - m_blockingThread = Thread.currentThread(); - } - } - - private void clearBlockingThread() - { - synchronized( this ) { - Assert.assertTrue( null != m_blockingThread ); - m_blockingThread = null; - } - } - - private void interruptBlockingThread() - { - synchronized( this ) { - if ( null != m_blockingThread ) { - m_blockingThread.interrupt(); - } - } - } - - private void releaseIfBlocking() - { - synchronized( this ) { - if ( null != m_blockingThread ) { - m_forResultWait.release(); - } - } - } - - private void handleConndMessage( String room, int devOrder, // <- hostID - boolean allHere, int nMissing ) - { - int naMsg = 0; - int naKey = 0; - String toastStr = null; - if ( allHere ) { - // All players have now joined the game. The device that - // created the room will assign tiles. Then it will be - // the first player's turn - - // Skip this until there's a way to show it only once per game - if ( false ) { - toastStr = getString( R.string.msg_relay_all_heref, room ); - if ( devOrder > 1 ) { - naMsg = R.string.not_again_conndall; - naKey = R.string.key_notagain_conndall; - } - } - } else if ( nMissing > 0 ) { - if ( !m_haveInvited ) { - m_haveInvited = true; - m_room = room; - m_missing = nMissing; - showDialog( DlgID.DLG_INVITE.ordinal() ); - ABUtils.invalidateOptionsMenuIf( this ); - } else { - toastStr = getString( R.string.msg_relay_waiting, devOrder, - room, nMissing ); - if ( devOrder == 1 ) { - naMsg = R.string.not_again_conndfirst; - naKey = R.string.key_notagain_conndfirst; - } else { - naMsg = R.string.not_again_conndmid; - naKey = R.string.key_notagain_conndmid; - } - } - } - - if ( null != toastStr ) { - m_toastStr = toastStr; - if ( naMsg == 0 ) { - dlgButtonClicked( Action.SHOW_EXPL_ACTION, - AlertDialog.BUTTON_POSITIVE, null ); - } else { - showNotAgainDlgThen( naMsg, naKey, Action.SHOW_EXPL_ACTION ); - } - } - - m_missing = nMissing; - ABUtils.invalidateOptionsMenuIf( this ); - } // handleConndMessage - - private class BoardUtilCtxt extends UtilCtxtImpl { - - public BoardUtilCtxt() - { - super( BoardActivity.this ); - } - - @Override - public void requestTime() - { - post( new Runnable() { - public void run() { - if ( null != m_jniThread ) { - m_jniThread.handleBkgrnd( JNICmd.CMD_DO ); - } - } - } ); - } - - @Override - public void remSelected() - { - m_jniThread.handle( JNICmd.CMD_REMAINING, - R.string.tiles_left_title ); - } - - @Override - public void setIsServer( boolean isServer ) - { - DeviceRole newRole = isServer? DeviceRole.SERVER_ISSERVER - : DeviceRole.SERVER_ISCLIENT; - if ( newRole != m_gi.serverRole ) { - m_gi.serverRole = newRole; - if ( !isServer ) { - m_jniThread.handle( JNICmd.CMD_SWITCHCLIENT ); - } - } - } - - @Override - public void bonusSquareHeld( int bonus ) - { - int id = 0; - switch( bonus ) { - case BONUS_DOUBLE_LETTER: - id = R.string.bonus_l2x; - break; - case BONUS_DOUBLE_WORD: - id = R.string.bonus_w2x; - break; - case BONUS_TRIPLE_LETTER: - id = R.string.bonus_l3x; - break; - case BONUS_TRIPLE_WORD: - id = R.string.bonus_w3x; - break; - default: - Assert.fail(); - } - - if ( 0 != id ) { - final String bonusStr = getString( id ); - post( new Runnable() { - public void run() { - Utils.showToast( BoardActivity.this, bonusStr ); - } - } ); - } - } - - @Override - public void playerScoreHeld( int player ) - { - String expl = XwJNI.model_getPlayersLastScore( m_jniGamePtr, - player ); - if ( expl.length() == 0 ) { - expl = getString( R.string.no_moves_made ); - } - String name = m_gi.players[player].name; - final String text = String.format( "%s\n%s", name, expl ); - post( new Runnable() { - public void run() { - Utils.showToast( BoardActivity.this, text ); - } - } ); - } - - @Override - public void cellSquareHeld( final String words ) - { - post( new Runnable() { - public void run() { - launchLookup( wordsToArray( words ), m_gi.dictLang ); - } - } ); - } - - @Override - public void setTimer( int why, int when, int handle ) - { - if ( null != m_timers[why] ) { - removeCallbacks( m_timers[why] ); - } - - m_timers[why] = new TimerRunnable( why, when, handle ); - - int inHowLong; - switch ( why ) { - case UtilCtxt.TIMER_COMMS: - inHowLong = when * 1000; - break; - case UtilCtxt.TIMER_TIMERTICK: - inHowLong = 1000; // when is 0 for TIMER_TIMERTICK - break; - default: - inHowLong = 500; - } - postDelayed( m_timers[why], inHowLong ); - } - - @Override - public void clearTimer( int why ) - { - if ( null != m_timers[why] ) { - removeCallbacks( m_timers[why] ); - m_timers[why] = null; - } - } - - // This is supposed to be called from the jni thread - @Override - public int userPickTileBlank( int playerNum, String[] texts) - { - m_texts = texts; - waitBlockingDialog( DlgID.PICK_TILE_REQUESTBLANK_BLK, 0 ); - return m_resultCode; - } - - @Override - public int userPickTileTray( int playerNum, String[] texts, - String[] curTiles, int nPicked ) - { - m_texts = texts; - m_curTiles = TextUtils.join( ", ", curTiles ); - m_canUndoTiles = 0 < nPicked; - waitBlockingDialog( DlgID.PICK_TILE_REQUESTTRAY_BLK, - UtilCtxt.PICKER_PICKALL ); - return m_resultCode; - } - - @Override - public String askPassword( String name ) - { - // call this each time dlg created or will get exception - // for reusing m_passwdLyt - m_pwdName = name; - setupPasswdVars(); - - waitBlockingDialog( DlgID.ASK_PASSWORD_BLK, 0 ); - - String result = null; // means cancelled - if ( 0 != m_resultCode ) { - result = m_passwdEdit.getText().toString(); - } - return result; - } - - @Override - public void turnChanged( int newTurn ) - { - if ( 0 <= newTurn ) { - post( new Runnable() { - public void run() { - showNotAgainDlgThen( R.string.not_again_turnchanged, - R.string.key_notagain_turnchanged ); - } - } ); - m_jniThread.handle( JNICmd. CMD_ZOOM, -8 ); - } - } - - @Override - public boolean engineProgressCallback() - { - return ! m_jniThread.busy(); - } - - @Override - public boolean userQuery( int id, String query ) - { - boolean result; - - switch( id ) { - // Though robot-move dialogs don't normally need to block, - // if the player after this one is also a robot and we - // don't block then a second dialog will replace this one. - // So block. Yuck. - case UtilCtxt.QUERY_ROBOT_TRADE: - m_dlgBytes = query; - m_dlgTitle = R.string.info_title; - waitBlockingDialog( DlgID.QUERY_INFORM_BLK, 0 ); - result = true; - break; - - // These *are* blocking dialogs - case UtilCtxt.QUERY_COMMIT_TURN: - m_dlgBytes = query; - m_dlgTitle = R.string.query_title; - result = 0 != waitBlockingDialog( DlgID.QUERY_REQUEST_BLK, 0 ); - break; - default: - Assert.fail(); - result = false; - } - - return result; - } - - @Override - public boolean confirmTrade( String[] tiles ) - { - m_dlgTitle = R.string.info_title; - m_dlgBytes = getString( R.string.query_tradef, - TextUtils.join( ", ", tiles ) ); - return 0 != waitBlockingDialog( DlgID.QUERY_REQUEST_BLK, 0 ); - } - - @Override - public void userError( int code ) - { - int resid = 0; - switch( code ) { - case UtilCtxt.ERR_TILES_NOT_IN_LINE: - resid = R.string.str_tiles_not_in_line; - break; - case UtilCtxt.ERR_NO_EMPTIES_IN_TURN: - resid = R.string.str_no_empties_in_turn; - break; - case UtilCtxt.ERR_TWO_TILES_FIRST_MOVE: - resid = R.string.str_two_tiles_first_move; - break; - case UtilCtxt.ERR_TILES_MUST_CONTACT: - resid = R.string.str_tiles_must_contact; - break; - case UtilCtxt.ERR_NOT_YOUR_TURN: - resid = R.string.str_not_your_turn; - break; - case UtilCtxt.ERR_NO_PEEK_ROBOT_TILES: - resid = R.string.str_no_peek_robot_tiles; - break; - case UtilCtxt.ERR_NO_EMPTY_TRADE: - // This should not be possible as the button's - // disabled when no tiles selected. - Assert.fail(); - break; - case UtilCtxt.ERR_TOO_FEW_TILES_LEFT_TO_TRADE: - resid = R.string.str_too_few_tiles_left_to_trade; - break; - case UtilCtxt.ERR_CANT_UNDO_TILEASSIGN: - resid = R.string.str_cant_undo_tileassign; - break; - case UtilCtxt.ERR_CANT_HINT_WHILE_DISABLED: - resid = R.string.str_cant_hint_while_disabled; - break; - case UtilCtxt.ERR_NO_PEEK_REMOTE_TILES: - resid = R.string.str_no_peek_remote_tiles; - break; - case UtilCtxt.ERR_REG_UNEXPECTED_USER: - resid = R.string.str_reg_unexpected_user; - break; - case UtilCtxt.ERR_SERVER_DICT_WINS: - resid = R.string.str_server_dict_wins; - break; - case ERR_REG_SERVER_SANS_REMOTE: - resid = R.string.str_reg_server_sans_remote; - break; - } - - if ( resid != 0 ) { - nonBlockingDialog( DlgID.DLG_OKONLY, getString( resid ) ); - } - } // userError - - @Override - public void informMissing( boolean isServer, CommsConnType connType, - final int nMissingPlayers ) - { - m_connType = connType; - - Action action = null; - if ( 0 < nMissingPlayers && isServer && !m_haveInvited ) { - switch( connType ) { - case COMMS_CONN_BT: - action = Action.BT_PICK_ACTION; - break; - case COMMS_CONN_SMS: - action = Action.SMS_PICK_ACTION; - break; - } - } - if ( null != action ) { - m_haveInvited = true; - final Action faction = action; - final String fmsg = getString( R.string.invite_msgf, - nMissingPlayers ); - post( new Runnable() { - public void run() { - DbgUtils.showf( BoardActivity.this, - getString( R.string.players_missf, - nMissingPlayers ) ); - m_nMissingPlayers = nMissingPlayers; - showConfirmThen( fmsg, faction ); - } - } ); - } - } - - @Override - public void informMove( String expl, String words ) - { - m_words = null == words? null : wordsToArray( words ); - nonBlockingDialog( DlgID.DLG_SCORES, expl ); - } - - @Override - public void informUndo() - { - nonBlockingDialog( DlgID.DLG_OKONLY, getString( R.string.remote_undone ) ); - } - - @Override - public void informNetDict( int code, String oldName, - String newName, String newSum, - CurGameInfo.XWPhoniesChoice phonies ) - { - // If it's same dict and same sum, we're good. That - // should be the normal case. Otherwise: if same name but - // different sum, notify and offer to upgrade. If - // different name, offer to install. - String msg = null; - if ( oldName.equals( newName ) ) { - String oldSum = DictLangCache.getDictMD5Sum( BoardActivity.this, - oldName ); - if ( !oldSum.equals( newSum ) ) { - // Same dict, different versions - msg = getString( R.string.inform_dict_diffversionf, - oldName ); - } - } else { - // Different dict! If we have the other one, switch - // to it. Otherwise offer to download - DlgID dlgID; - msg = getString( R.string.inform_dict_diffdictf, - oldName, newName, newName ); - if ( DictLangCache.haveDict( BoardActivity.this, code, - newName ) ) { - dlgID = DlgID.DLG_USEDICT; - } else { - dlgID = DlgID.DLG_GETDICT; - msg += getString( R.string.inform_dict_download ); - } - m_getDict = newName; - nonBlockingDialog( dlgID, msg ); - } - } - - @Override - public void notifyGameOver() - { - m_gameOver = true; - m_jniThread.handle( JNICmd.CMD_POST_OVER ); - } - - // public void yOffsetChange( int maxOffset, int oldOffset, int newOffset ) - // { - // DbgUtils.logf( "yOffsetChange(maxOffset=%d)", maxOffset ); - // m_view.setVerticalScrollBarEnabled( maxOffset > 0 ); - // } - @Override - public boolean warnIllegalWord( String dict, String[] words, int turn, - boolean turnLost ) - { - boolean accept = turnLost; - - String wordsString = TextUtils.join( ", ", words ); - String message = - getString( R.string.ids_badwordsf, wordsString, dict ); - - if ( turnLost ) { - m_dlgBytes = message + getString( R.string.badwords_lost ); - m_dlgTitle = R.string.badwords_title; - waitBlockingDialog( DlgID.DLG_BADWORDS_BLK, 0 ); - } else { - m_dlgBytes = message + getString( R.string.badwords_accept ); - m_dlgTitle = R.string.query_title; - accept = 0 != waitBlockingDialog( DlgID.QUERY_REQUEST_BLK, 0 ); - } - - return accept; - } - - // Let's have this block in case there are multiple messages. If - // we don't block the jni thread will continue processing messages - // and may stack dialogs on top of this one. Including later - // chat-messages. - @Override - public void showChat( final String msg ) - { - if ( BuildConstants.CHAT_SUPPORTED ) { - post( new Runnable() { - public void run() { - DBUtils.appendChatHistory( BoardActivity.this, - m_rowid, msg, false ); - startChatActivity(); - } - } ); - } - } - } // class BoardUtilCtxt - - private void loadGame() - { - if ( 0 == m_jniGamePtr ) { - try { - String[] dictNames = GameUtils.dictNames( this, m_rowid ); - DictUtils.DictPairs pairs = DictUtils.openDicts( this, dictNames ); - - if ( pairs.anyMissing( dictNames ) ) { - showDictGoneFinish(); - } else { - Assert.assertNull( m_gameLock ); - m_gameLock = new GameLock( m_rowid, true ).lock(); - - byte[] stream = GameUtils.savedGame( this, m_gameLock ); - m_gi = new CurGameInfo( this ); - XwJNI.gi_from_stream( m_gi, stream ); - String langName = m_gi.langName(); - - setThis( this ); - - m_jniGamePtr = XwJNI.initJNI(); - - if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) { - m_xport = new CommsTransport( m_jniGamePtr, this, this, - m_rowid, m_gi.serverRole ); - } - - CommonPrefs cp = CommonPrefs.get( this ); - if ( null == stream || - ! XwJNI.game_makeFromStream( m_jniGamePtr, stream, - m_gi, dictNames, - pairs.m_bytes, - pairs.m_paths, langName, - m_utils, m_jniu, - null, cp, m_xport ) ) { - XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, - m_jniu, null, cp, m_xport, - dictNames, pairs.m_bytes, - pairs.m_paths, langName ); - } - - m_summary = new GameSummary( this, m_gi ); - XwJNI.game_summarize( m_jniGamePtr, m_summary ); - - Handler handler = new Handler() { - public void handleMessage( Message msg ) { - switch( msg.what ) { - case JNIThread.DIALOG: - m_dlgBytes = (String)msg.obj; - m_dlgTitle = msg.arg1; - showDialog( DlgID.DLG_OKONLY.ordinal() ); - break; - case JNIThread.QUERY_ENDGAME: - showDialog( DlgID.QUERY_ENDGAME.ordinal() ); - break; - case JNIThread.TOOLBAR_STATES: - if ( null != m_jniThread ) { - m_gsi = - m_jniThread.getGameStateInfo(); - updateToolbar(); - if ( m_inTrade != m_gsi.inTrade ) { - m_inTrade = m_gsi.inTrade; - } - m_view.setInTrade( m_inTrade ); - adjustTradeVisibility(); - Activity self = BoardActivity.this; - ABUtils.invalidateOptionsMenuIf( self ); - } - break; - case JNIThread.GOT_WORDS: - launchLookup( wordsToArray((String)msg.obj), - m_gi.dictLang ); - break; - case JNIThread.GAME_OVER: - m_dlgBytes = (String)msg.obj; - m_dlgTitle = msg.arg1; - showDialog( DlgID.GAME_OVER.ordinal() ); - break; - } - } - }; - m_jniThread = - new JNIThread( m_jniGamePtr, stream, m_gi, - m_view, m_gameLock, this, handler ); - // see http://stackoverflow.com/questions/680180/where-to-stop-\ - // destroy-threads-in-android-service-class - m_jniThread.setDaemon( true ); - m_jniThread.start(); - - m_view.startHandling( this, m_jniThread, m_jniGamePtr, m_gi, - m_connType ); - if ( null != m_xport ) { - m_xport.setReceiver( m_jniThread, m_handler ); - } - m_jniThread.handle( JNICmd.CMD_START ); - - if ( !CommonPrefs.getHideTitleBar( this ) ) { - setTitle( GameUtils.getName( this, m_rowid ) ); - } - m_toolbar = new Toolbar( this, R.id.toolbar_horizontal ); - - populateToolbar(); - adjustTradeVisibility(); - - int flags = DBUtils.getMsgFlags( this, m_rowid ); - if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) { - startChatActivity(); - } - if ( m_overNotShown ) { - boolean auto = false; - if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) { - m_gameOver = true; - } else if ( DBUtils.gameOver( this, m_rowid ) ) { - m_gameOver = true; - auto = true; - } - if ( m_gameOver ) { - m_overNotShown = false; - m_jniThread.handle( JNICmd.CMD_POST_OVER, auto ); - } - } - if ( 0 != flags ) { - DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE ); - } - - if ( null != m_xport ) { - warnIfNoTransport(); - trySendChats(); - Utils.cancelNotification( this, (int)m_rowid ); - m_xport.tickle( m_connType ); - tryInvites(); - } - } - } catch ( GameUtils.NoSuchGameException nsge ) { - DbgUtils.loge( nsge ); - finish(); - } - } - } // loadGame - - private void checkAndHandle( JNICmd cmd ) - { - if ( null != m_jniThread ) { - m_jniThread.handle( cmd ); - } - } - - private void populateToolbar() - { - m_toolbar.setListener( Toolbar.BUTTON_BROWSE_DICT, - R.string.not_again_browseall, - R.string.key_na_browseall, - Action.BUTTON_BROWSEALL_ACTION ); - m_toolbar.setLongClickListener( Toolbar.BUTTON_BROWSE_DICT, - R.string.not_again_browse, - R.string.key_na_browse, - Action.BUTTON_BROWSE_ACTION ); - m_toolbar.setListener( Toolbar.BUTTON_HINT_PREV, - R.string.not_again_hintprev, - R.string.key_notagain_hintprev, - Action.PREV_HINT_ACTION ); - m_toolbar.setListener( Toolbar.BUTTON_HINT_NEXT, - R.string.not_again_hintnext, - R.string.key_notagain_hintnext, - Action.NEXT_HINT_ACTION ); - m_toolbar.setListener( Toolbar.BUTTON_JUGGLE, - R.string.not_again_juggle, - R.string.key_notagain_juggle, - Action.JUGGLE_ACTION ); - m_toolbar.setListener( Toolbar.BUTTON_FLIP, - R.string.not_again_flip, - R.string.key_notagain_flip, - Action.FLIP_ACTION ); - m_toolbar.setListener( Toolbar.BUTTON_ZOOM, - R.string.not_again_zoom, - R.string.key_notagain_zoom, - Action.ZOOM_ACTION ); - m_toolbar.setListener( Toolbar.BUTTON_VALUES, - R.string.not_again_values, - R.string.key_na_values, - Action.VALUES_ACTION ); - m_toolbar.setListener( Toolbar.BUTTON_UNDO, - R.string.not_again_undo, - R.string.key_notagain_undo, - Action.UNDO_ACTION ); - if ( BuildConstants.CHAT_SUPPORTED ) { - m_toolbar.setListener( Toolbar.BUTTON_CHAT, - R.string.not_again_chat, - R.string.key_notagain_chat, - Action.CHAT_ACTION ); - } - } // populateToolbar - - private OnDismissListener makeODLforBlocking( final int id ) - { - return new OnDismissListener() { - public void onDismiss( DialogInterface di ) { - releaseIfBlocking(); - removeDialog( id ); - } - }; - } - - private int waitBlockingDialog( final DlgID dlgID, int cancelResult ) - { - int result = cancelResult; - // this has been true; dunno why - if ( DlgID.NONE != m_blockingDlgID ) { - DbgUtils.logf( "waitBlockingDialog: dropping dlgID %d b/c %d set", - dlgID, m_blockingDlgID ); - } else { - setBlockingThread(); - m_resultCode = cancelResult; - - if ( post( new Runnable() { - public void run() { - m_blockingDlgID = dlgID; - showDialog( dlgID.ordinal() ); - } - } ) ) { - - try { - m_forResultWait.acquire(); - } catch ( java.lang.InterruptedException ie ) { - DbgUtils.loge( ie ); - if ( DlgID.NONE != m_blockingDlgID ) { - try { - dismissDialog( m_blockingDlgID.ordinal() ); - } catch ( java.lang.IllegalArgumentException iae ) { - DbgUtils.loge( iae ); - } - } - } - m_blockingDlgID = DlgID.NONE; - } - - clearBlockingThread(); - result = m_resultCode; - } - return result; - } - - private void nonBlockingDialog( final DlgID dlgID, String txt ) - { - switch ( dlgID ) { - case DLG_OKONLY: - case DLG_SCORES: - m_dlgTitle = R.string.info_title; - break; - case DLG_USEDICT: - case DLG_GETDICT: - m_dlgTitle = R.string.inform_dict_title; - break; - - default: - Assert.fail(); - } - - m_dlgBytes = txt; - post( new Runnable() { - public void run() { - showDialog( dlgID.ordinal() ); - } - } ); - } - - private boolean doZoom( int zoomBy ) - { - boolean handled = null != m_jniThread; - if ( handled ) { - m_jniThread.handle( JNICmd.CMD_ZOOM, zoomBy ); - } - return handled; - } - - private void startChatActivity() - { - if ( BuildConstants.CHAT_SUPPORTED ) { - Intent intent = new Intent( this, ChatActivity.class ); - intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_rowid ); - startActivityForResult( intent, CHAT_REQUEST ); - } - } - - private void waitCloseGame( boolean save ) - { - if ( 0 != m_jniGamePtr ) { - if ( null != m_xport ) { - m_xport.waitToStop(); - m_xport = null; - } - - interruptBlockingThread(); - - if ( null != m_jniThread ) { - m_jniThread.waitToStop( save ); - m_jniThread = null; - } - m_view.stopHandling(); - - clearThis( this ); - - if ( XWPrefs.getThumbEnabled( this ) ) { - // Before we dispose, and after JNIThread has - // relinquished interest, redraw on smaller scale. - Bitmap thumb = - GameUtils.takeSnapshot( this, m_jniGamePtr, m_gi ); - DBUtils.saveThumbnail( this, m_gameLock, thumb ); - } - - XwJNI.game_dispose( m_jniGamePtr ); - m_jniGamePtr = 0; - m_gi = null; - - m_gameLock.unlock(); - m_gameLock = null; - } - } - - private void warnIfNoTransport() - { - switch( m_connType ) { - case COMMS_CONN_SMS: - if ( XWApp.SMSSUPPORTED && !XWPrefs.getSMSEnabled( this ) ) { - showConfirmThen( R.string.warn_sms_disabled, - R.string.newgame_enable_sms, - Action.SMS_CONFIG_ACTION ); - } - break; - } - } - - private void trySendChats() - { - if ( BuildConstants.CHAT_SUPPORTED && null != m_jniThread ) { - Iterator iter = m_pendingChats.iterator(); - while ( iter.hasNext() ) { - m_jniThread.handle( JNICmd.CMD_SENDCHAT, iter.next() ); - } - m_pendingChats.clear(); - } - } - - private void tryInvites() - { - if ( XWApp.BTSUPPORTED || XWApp.SMSSUPPORTED ) { - if ( null != m_missingDevs ) { - String gameName = GameUtils.getName( this, m_rowid ); - boolean doProgress = false; - m_invitesPending = m_missingDevs.length; - for ( String dev : m_missingDevs ) { - switch( m_connType ) { - case COMMS_CONN_BT: - BTService.inviteRemote( this, dev, m_gi.gameID, - gameName, m_gi.dictLang, - m_gi.dictName, m_gi.nPlayers, - 1 ); - break; - case COMMS_CONN_SMS: - SMSService.inviteRemote( this, dev, m_gi.gameID, - gameName, m_gi.dictLang, - m_gi.dictName, m_gi.nPlayers, - 1 ); - break; - } - } - if ( doProgress ) { - startProgress( R.string.invite_progress ); - } - m_missingDevs = null; - } - } - } - - private void updateToolbar() - { - m_toolbar.update( Toolbar.BUTTON_FLIP, m_gsi.visTileCount >= 1 ); - m_toolbar.update( Toolbar.BUTTON_VALUES, m_gsi.visTileCount >= 1 ); - m_toolbar.update( Toolbar.BUTTON_JUGGLE, m_gsi.canShuffle ); - m_toolbar.update( Toolbar.BUTTON_UNDO, m_gsi.canRedo ); - m_toolbar.update( Toolbar.BUTTON_HINT_PREV, m_gsi.canHint ); - m_toolbar.update( Toolbar.BUTTON_HINT_NEXT, m_gsi.canHint ); - m_toolbar.update( Toolbar.BUTTON_CHAT, - BuildConstants.CHAT_SUPPORTED && m_gsi.canChat ); - m_toolbar.update( Toolbar.BUTTON_BROWSE_DICT, - null != m_gi.dictName( m_view.getCurPlayer() ) ); - } - - private void adjustTradeVisibility() - { - m_toolbar.setVisibility( m_inTrade? View.GONE : View.VISIBLE ); - if ( null != m_tradeButtons ) { - m_tradeButtons.setVisibility( m_inTrade? View.VISIBLE : View.GONE ); - } - if ( m_inTrade && null != m_exchCommmitButton ) { - m_exchCommmitButton.setEnabled( m_gsi.tradeTilesSelected ); - } - } - - private void setBackgroundColor() - { - View view = findViewById( R.id.board_root ); - // Google's reported an NPE here, so test - if ( null != view ) { - int back = CommonPrefs.get( this ) - .otherColors[CommonPrefs.COLOR_BACKGRND]; - view.setBackgroundColor( back ); - } - } - - private void setKeepScreenOn() - { - boolean keepOn = CommonPrefs.getKeepScreenOn( this ); - m_view.setKeepScreenOn( keepOn ); - - if ( keepOn ) { - if ( null == m_screenTimer ) { - m_screenTimer = new Runnable() { - public void run() { - if ( null != m_view ) { - m_view.setKeepScreenOn( false ); - } - } - }; - } - removeCallbacks( m_screenTimer ); // needed? - postDelayed( m_screenTimer, SCREEN_ON_TIME ); - } - } - - @Override - protected boolean post( Runnable runnable ) - { - boolean canPost = null != m_handler; - if ( canPost ) { - m_handler.post( runnable ); - } else { - DbgUtils.logf( "post: dropping because handler null" ); - } - return canPost; - } - - private void postDelayed( Runnable runnable, int when ) - { - if ( null != m_handler ) { - m_handler.postDelayed( runnable, when ); - } else { - DbgUtils.logf( "postDelayed: dropping %d because handler null", when ); - } - } - - private void removeCallbacks( Runnable which ) - { - if ( null != m_handler ) { - m_handler.removeCallbacks( which ); - } else { - DbgUtils.logf( "removeCallbacks: dropping %h because handler null", - which ); - } - } - - private String[] wordsToArray( String words ) - { - String[] tmp = TextUtils.split( words, "\n" ); - String[] wordsArray = new String[tmp.length]; - for ( int ii = 0, jj = tmp.length; ii < tmp.length; ++ii, --jj ) { - wordsArray[ii] = tmp[jj-1]; - } - return wordsArray; - } - - private void setupPasswdVars() - { - m_dlgTitleStr = getString( R.string.msg_ask_password, m_pwdName ); - m_passwdLyt = (LinearLayout)Utils.inflate( BoardActivity.this, - R.layout.passwd_view ); - m_passwdEdit = (EditText)m_passwdLyt.findViewById( R.id.edit ); - } - - private void doRematch() - { - Intent intent = GamesListActivity.makeRematchIntent( this, m_gi, m_rowid ); - if ( null != intent ) { - startActivity( intent ); - finish(); - } - } - - private Button setListenerOrHide( int id ) - { - Button button = (Button)findViewById( id ); - if ( null != button ) { - if ( ABUtils.haveActionBar() ) { - button.setVisibility( View.INVISIBLE ); - } else { - button.setOnClickListener( this ); - } - } - return button; + return m_dlgt.onOptionsItemSelected( item ) + || super.onOptionsItemSelected( item ); } private static void noteSkip() diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java new file mode 100644 index 000000000..f1d77b032 --- /dev/null +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java @@ -0,0 +1,2273 @@ +/* -*- compile-command: "find-and-ant.sh debug install"; -*- */ +/* + * Copyright 2009 - 2014 by Eric House (xwords@eehouse.org). All + * rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +package org.eehouse.android.xw4; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; + +import android.content.DialogInterface.OnDismissListener; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; + +import android.graphics.Bitmap; +import android.graphics.Rect; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.text.TextUtils; + +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.HashSet; +import java.util.concurrent.Semaphore; +import junit.framework.Assert; + +import org.eehouse.android.xw4.DlgDelegate.Action; +import org.eehouse.android.xw4.jni.*; +import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; +import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; +import org.eehouse.android.xw4.jni.JNIThread.*; + + +public class BoardDelegate extends DelegateBase + implements TransportProcs.TPMsgHandler, View.OnClickListener, + DictImportActivity.DownloadFinishedListener, + ConnStatusHandler.ConnStatusCBacks, + NFCUtils.NFCActor { + + public static final String INTENT_KEY_CHAT = "chat"; + + private static final int CHAT_REQUEST = 1; + private static final int BT_INVITE_RESULT = 2; + private static final int SMS_INVITE_RESULT = 3; + + private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins + + private static final String DLG_TITLE = "DLG_TITLE"; + private static final String DLG_TITLESTR = "DLG_TITLESTR"; + private static final String DLG_BYTES = "DLG_BYTES"; + private static final String ROOM = "ROOM"; + private static final String PWDNAME = "PWDNAME"; + private static final String TOASTSTR = "TOASTSTR"; + private static final String WORDS = "WORDS"; + private static final String GETDICT = "GETDICT"; + + private Activity m_activity; + private BoardView m_view; + private int m_jniGamePtr; + private GameLock m_gameLock; + private CurGameInfo m_gi; + private GameSummary m_summary; + private CommsTransport m_xport; + private Handler m_handler = null; + private TimerRunnable[] m_timers; + private Runnable m_screenTimer; + private long m_rowid; + private Toolbar m_toolbar; + private View m_tradeButtons; + private Button m_exchCommmitButton; + private Button m_exchCancelButton; + + private ArrayList m_pendingChats; + + private String m_dlgBytes = null; + private EditText m_passwdEdit = null; + private LinearLayout m_passwdLyt = null; + private int m_dlgTitle; + private String m_dlgTitleStr; + private String[] m_texts; + private CommsConnType m_connType = CommsConnType.COMMS_CONN_NONE; + private String[] m_missingDevs; + private String m_curTiles; + private boolean m_canUndoTiles; + private boolean m_firingPrefs; + private JNIUtils m_jniu; + private boolean m_volKeysZoom; + private boolean m_inTrade; // save this in bundle? + private BoardUtilCtxt m_utils; + private int m_nMissingPlayers = -1; + private int m_invitesPending; + private boolean m_gameOver = false; + + // call startActivityForResult synchronously + private Semaphore m_forResultWait = new Semaphore(0); + private int m_resultCode; + + private Thread m_blockingThread; + private JNIThread m_jniThread; + private JNIThread.GameStateInfo m_gsi; + private DlgID m_blockingDlgID = DlgID.NONE; + + private String m_room; + private String m_toastStr; + private String[] m_words; + private String m_pwdName; + private String m_getDict; + + private int m_missing; + private boolean m_haveInvited = false; + private boolean m_overNotShown; + + private static HashSet s_this = new HashSet(); + + public static boolean feedMessage( int gameID, byte[] msg, + CommsAddrRec retAddr ) + { + boolean delivered = false; + int size; + synchronized( s_this ) { + size = s_this.size(); + if ( 1 == size ) { + BoardDelegate self = s_this.iterator().next(); + Assert.assertNotNull( self.m_gi ); + Assert.assertNotNull( self.m_gameLock ); + Assert.assertNotNull( self.m_jniThread ); + if ( gameID == self.m_gi.gameID ) { + self.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, retAddr ); + delivered = true; + } + } + } + + if ( 1 < s_this.size() ) { + noteSkip(); + } + return delivered; + } + + public static boolean feedMessage( long rowid, byte[] msg ) + { + return feedMessages( rowid, new byte[][]{msg} ); + } + + public static boolean feedMessages( long rowid, byte[][] msgs ) + { + boolean delivered = false; + Assert.assertNotNull( msgs ); + int size; + synchronized( s_this ) { + size = s_this.size(); + if ( 1 == size ) { + BoardDelegate self = s_this.iterator().next(); + Assert.assertNotNull( self.m_gi ); + Assert.assertNotNull( self.m_gameLock ); + Assert.assertNotNull( self.m_jniThread ); + if ( rowid == self.m_rowid ) { + delivered = true; // even if no messages! + for ( byte[] msg : msgs ) { + self.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg, + null ); + } + } + } + } + if ( 1 < size ) { + noteSkip(); + } + return delivered; + } + + private static void setThis( BoardDelegate self ) + { + synchronized( s_this ) { + Assert.assertTrue( !s_this.contains(self) ); // here + s_this.add( self ); + } + } + + private static void clearThis( BoardDelegate self ) + { + synchronized( s_this ) { + Assert.assertTrue( s_this.contains( self ) ); + s_this.remove( self ); + } + } + + public class TimerRunnable implements Runnable { + private int m_why; + private int m_when; + private int m_handle; + private TimerRunnable( int why, int when, int handle ) { + m_why = why; + m_when = when; + m_handle = handle; + } + public void run() { + m_timers[m_why] = null; + if ( null != m_jniThread ) { + m_jniThread.handleBkgrnd( JNICmd.CMD_TIMER_FIRED, + m_why, m_when, m_handle ); + } + } + } + + protected Dialog createDialog( int id ) + { + Dialog dialog = super.createDialog( id ); + if ( null == dialog ) { + DialogInterface.OnClickListener lstnr; + AlertDialog.Builder ab; + + final DlgID dlgID = DlgID.values()[id]; + switch ( dlgID ) { + case DLG_OKONLY: + case DLG_RETRY: + case GAME_OVER: + case DLG_CONNSTAT: + ab = new AlertDialog.Builder( m_activity ) + .setTitle( m_dlgTitle ) + .setMessage( m_dlgBytes ) + .setPositiveButton( R.string.button_ok, null ); + if ( DlgID.DLG_RETRY == dlgID ) { + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dlg, + int whichButton ) { + m_jniThread.handle( JNICmd.CMD_RESET ); + } + }; + ab.setNegativeButton( R.string.button_retry, lstnr ); + } else if ( XWApp.REMATCH_SUPPORTED && DlgID.GAME_OVER == dlgID ) { + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dlg, + int whichButton ) { + doRematch(); + } + }; + ab.setNegativeButton( R.string.button_rematch, lstnr ); + } else if ( DlgID.DLG_CONNSTAT == dlgID && + CommsConnType.COMMS_CONN_RELAY == m_connType ) { + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dlg, + int whichButton ) { + RelayService.reset( m_activity ); + } + }; + ab.setNegativeButton( R.string.button_reconnect, lstnr ); + } + dialog = ab.create(); + Utils.setRemoveOnDismiss( m_activity, dialog, dlgID ); + break; + + case DLG_USEDICT: + case DLG_GETDICT: + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dlg, + int whichButton ) { + if ( DlgID.DLG_USEDICT == dlgID ) { + setGotGameDict( m_getDict ); + } else { + DictImportActivity + .downloadDictInBack( m_activity, + m_gi.dictLang, + m_getDict, + BoardDelegate.this ); + } + } + }; + dialog = new AlertDialog.Builder( m_activity ) + .setTitle( m_dlgTitle ) + .setMessage( m_dlgBytes ) + .setPositiveButton( R.string.button_yes, lstnr ) + .setNegativeButton( R.string.button_no, null ) + .create(); + Utils.setRemoveOnDismiss( m_activity, dialog, dlgID ); + break; + + case DLG_DELETED: + ab = new AlertDialog.Builder( m_activity ) + .setTitle( R.string.query_title ) + .setMessage( R.string.msg_dev_deleted ) + .setPositiveButton( R.string.button_ok, null ); + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dlg, + int whichButton ) { + + waitCloseGame( false ); + GameUtils.deleteGame( m_activity, m_rowid, false ); + m_activity.finish(); + } + }; + ab.setNegativeButton( R.string.button_delete, lstnr ); + dialog = ab.create(); + break; + + case QUERY_REQUEST_BLK: + case QUERY_INFORM_BLK: + case DLG_SCORES: + case DLG_BADWORDS_BLK: + ab = new AlertDialog.Builder( m_activity ) + .setMessage( m_dlgBytes ); + if ( 0 != m_dlgTitle ) { + ab.setTitle( m_dlgTitle ); + } + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int whichButton ) { + m_resultCode = 1; + } + }; + ab.setPositiveButton( DlgID.QUERY_REQUEST_BLK == dlgID ? + R.string.button_yes : R.string.button_ok, + lstnr ); + if ( DlgID.QUERY_REQUEST_BLK == dlgID ) { + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int whichButton ) { + m_resultCode = 0; + } + }; + ab.setNegativeButton( R.string.button_no, lstnr ); + } else if ( DlgID.DLG_SCORES == dlgID ) { + if ( null != m_words && m_words.length > 0 ) { + String buttonTxt; + boolean studyOn = XWPrefs.getStudyEnabled( m_activity ); + if ( m_words.length == 1 ) { + int resID = studyOn + ? R.string.button_lookup_studyf + : R.string.button_lookupf; + buttonTxt = m_activity.getString( resID, m_words[0] ); + } else { + int resID = studyOn ? R.string.button_lookup_study + : R.string.button_lookup; + buttonTxt = m_activity.getString( resID ); + } + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int whichButton ) { + showNotAgainDlgThen( R.string. + not_again_lookup, + R.string. + key_na_lookup, + Action.LOOKUP_ACTION ); + } + }; + ab.setNegativeButton( buttonTxt, lstnr ); + } + } + + dialog = ab.create(); + dialog.setOnDismissListener( makeODLforBlocking( id ) ); + break; + + case PICK_TILE_REQUESTBLANK_BLK: + case PICK_TILE_REQUESTTRAY_BLK: + ab = new AlertDialog.Builder( m_activity ); + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int item ) { + m_resultCode = item; + } + }; + ab.setItems( m_texts, lstnr ); + + if ( DlgID.PICK_TILE_REQUESTBLANK_BLK == dlgID ) { + ab.setTitle( R.string.title_tile_picker ); + } else { + ab.setTitle( m_activity.getString( R.string.cur_tilesf, + m_curTiles ) ); + if ( m_canUndoTiles ) { + DialogInterface.OnClickListener undoClicked = + new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int whichButton ) { + m_resultCode = UtilCtxt.PICKER_BACKUP; + m_activity.removeDialog( dlgID.ordinal() ); + } + }; + ab.setPositiveButton( R.string.tilepick_undo, + undoClicked ); + } + DialogInterface.OnClickListener doAllClicked = + new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int whichButton ) { + m_resultCode = UtilCtxt.PICKER_PICKALL; + m_activity.removeDialog( dlgID.ordinal() ); + } + }; + ab.setNegativeButton( R.string.tilepick_all, doAllClicked ); + } + + dialog = ab.create(); + dialog.setOnDismissListener( makeODLforBlocking( id ) ); + break; + + case ASK_PASSWORD_BLK: + if ( null == m_passwdLyt ) { + setupPasswdVars(); + } + m_passwdEdit.setText( "", TextView.BufferType.EDITABLE ); + ab = new AlertDialog.Builder( m_activity ) + .setTitle( m_dlgTitleStr ) + .setView( m_passwdLyt ) + .setPositiveButton( R.string.button_ok, + new DialogInterface.OnClickListener() { + public void + onClick( DialogInterface dlg, + int whichButton ) { + m_resultCode = 1; + } + }); + dialog = ab.create(); + dialog.setOnDismissListener( makeODLforBlocking( id ) ); + break; + + case QUERY_ENDGAME: + dialog = new AlertDialog.Builder( m_activity ) + .setTitle( R.string.query_title ) + .setMessage( R.string.ids_endnow ) + .setPositiveButton( R.string.button_yes, + new DialogInterface.OnClickListener() { + public void + onClick( DialogInterface dlg, + int item ) { + m_jniThread. + handle(JNICmd.CMD_ENDGAME); + } + }) + .setNegativeButton( R.string.button_no, null ) + .create(); + break; + case DLG_INVITE: + if ( null != m_room ) { + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dialog, + int item ) { + showInviteChoicesThen( Action.LAUNCH_INVITE_ACTION ); + } + }; + dialog = new AlertDialog.Builder( m_activity ) + .setTitle( R.string.query_title ) + .setMessage( "" ) + .setPositiveButton( R.string.button_yes, lstnr ) + .setNegativeButton( R.string.button_no, null ) + .create(); + } + break; + + case ENABLE_NFC: + dialog = NFCUtils.makeEnableNFCDialog( m_activity ); + break; + + default: + // just drop it; super.onCreateDialog likely failed + break; + } + } + return dialog; + } // onCreateDialog + + protected void prepareDialog( int id, Dialog dialog ) + { + DbgUtils.logf( "BoardActivity:onPrepareDialog(id=%d)", id ); + DlgID dlgID = DlgID.values()[id]; + switch( dlgID ) { + case DLG_INVITE: + AlertDialog ad = (AlertDialog)dialog; + String message = m_activity.getString( R.string.invite_msgf, m_missing ); + if ( m_missing > 1 ) { + message += m_activity.getString( R.string.invite_multiple ); + } + ad.setMessage( message ); + break; + } + } + + public BoardDelegate( Activity activity, Bundle savedInstanceState ) + { + super( activity, savedInstanceState ); + m_activity = activity; + init( savedInstanceState ); + } + + private void init( Bundle savedInstanceState ) + { + getBundledData( savedInstanceState ); + + if ( CommonPrefs.getHideTitleBar( m_activity ) + && ABUtils.haveMenuKey( m_activity ) ) { + m_activity.requestWindowFeature( Window.FEATURE_NO_TITLE ); + } + + if ( BuildConstants.CHAT_SUPPORTED ) { + m_pendingChats = new ArrayList(); + } + + m_utils = new BoardUtilCtxt(); + m_jniu = JNIUtilsImpl.get( m_activity ); + m_activity.setContentView( R.layout.board ); + m_timers = new TimerRunnable[4]; // needs to be in sync with + // XWTimerReason + m_view = (BoardView)m_activity.findViewById( R.id.board_view ); + m_tradeButtons = m_activity.findViewById( R.id.exchange_buttons ); + m_exchCommmitButton = setListenerOrHide( R.id.exchange_commit ); + m_exchCancelButton = setListenerOrHide( R.id.exchange_cancel ); + m_volKeysZoom = XWPrefs.getVolKeysZoom( m_activity ); + + Intent intent = m_activity.getIntent(); + m_rowid = intent.getLongExtra( GameUtils.INTENT_KEY_ROWID, -1 ); + DbgUtils.logf( "BoardActivity: opening rowid %d", m_rowid ); + m_haveInvited = intent.getBooleanExtra( GameUtils.INVITED, false ); + m_overNotShown = true; + + NFCUtils.register( m_activity ); // Don't seem to need to unregister... + + setBackgroundColor(); + setKeepScreenOn(); + } // onCreate + + protected void onPause() + { + m_handler = null; + ConnStatusHandler.setHandler( null ); + waitCloseGame( true ); + } + + protected void onResume() + { + m_handler = new Handler(); + m_blockingDlgID = DlgID.NONE; + + setKeepScreenOn(); + + loadGame(); + + ConnStatusHandler.setHandler( this ); + } + + protected void onSaveInstanceState( Bundle outState ) + { + outState.putInt( DLG_TITLE, m_dlgTitle ); + outState.putString( DLG_TITLESTR, m_dlgTitleStr ); + outState.putString( DLG_BYTES, m_dlgBytes ); + outState.putString( ROOM, m_room ); + outState.putString( TOASTSTR, m_toastStr ); + outState.putStringArray( WORDS, m_words ); + outState.putString( PWDNAME, m_pwdName ); + outState.putString( GETDICT, m_getDict ); + } + + private void getBundledData( Bundle bundle ) + { + if ( null != bundle ) { + m_dlgTitleStr = bundle.getString( DLG_TITLESTR ); + m_dlgTitle = bundle.getInt( DLG_TITLE ); + m_dlgBytes = bundle.getString( DLG_BYTES ); + m_room = bundle.getString( ROOM ); + m_toastStr = bundle.getString( TOASTSTR ); + m_words = bundle.getStringArray( WORDS ); + m_pwdName = bundle.getString( PWDNAME ); + m_getDict = bundle.getString( GETDICT ); + } + } + + protected void onActivityResult( int requestCode, int resultCode, Intent data ) + { + if ( Activity.RESULT_CANCELED != resultCode ) { + switch ( requestCode ) { + case CHAT_REQUEST: + if ( BuildConstants.CHAT_SUPPORTED ) { + String msg = data.getStringExtra( INTENT_KEY_CHAT ); + if ( null != msg && msg.length() > 0 ) { + m_pendingChats.add( msg ); + trySendChats(); + } + } + break; + case BT_INVITE_RESULT: + // onActivityResult is called immediately *before* + // onResume -- meaning m_gi etc are still null. + m_missingDevs = data.getStringArrayExtra( BTInviteActivity.DEVS ); + break; + case SMS_INVITE_RESULT: + // onActivityResult is called immediately *before* + // onResume -- meaning m_gi etc are still null. + m_missingDevs = data.getStringArrayExtra( SMSInviteActivity.DEVS ); + break; + } + } + } + + protected void onWindowFocusChanged( boolean hasFocus ) + { + if ( hasFocus ) { + if ( m_firingPrefs ) { + m_firingPrefs = false; + m_volKeysZoom = XWPrefs.getVolKeysZoom( m_activity ); + if ( null != m_jniThread ) { + m_jniThread.handle( JNICmd.CMD_PREFS_CHANGE ); + } + // in case of change... + setBackgroundColor(); + setKeepScreenOn(); + } + } + } + + protected boolean onKeyDown( int keyCode, KeyEvent event ) + { + boolean handled = false; + if ( null != m_jniThread ) { + XwJNI.XP_Key xpKey = keyCodeToXPKey( keyCode ); + if ( XwJNI.XP_Key.XP_KEY_NONE != xpKey ) { + m_jniThread.handle( JNICmd.CMD_KEYDOWN, xpKey ); + } else { + switch( keyCode ) { + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_VOLUME_UP: + if ( m_volKeysZoom ) { + int zoomBy = KeyEvent.KEYCODE_VOLUME_DOWN == keyCode + ? -2 : 2; + handled = doZoom( zoomBy ); + } + break; + } + } + } + return handled; + } + + protected boolean onKeyUp( int keyCode, KeyEvent event ) + { + boolean handled = false; + if ( null != m_jniThread ) { + XwJNI.XP_Key xpKey = keyCodeToXPKey( keyCode ); + if ( XwJNI.XP_Key.XP_KEY_NONE != xpKey ) { + m_jniThread.handle( JNICmd.CMD_KEYUP, xpKey ); + handled = true; + } + } + return handled; + } + + protected boolean onPrepareOptionsMenu( Menu menu ) + { + boolean inTrade = false; + MenuItem item; + int strId; + + if ( null != m_gsi ) { + inTrade = m_gsi.inTrade; + menu.setGroupVisible( R.id.group_done, !inTrade ); + menu.setGroupVisible( R.id.group_exchange, inTrade ); + + if ( UtilCtxt.TRAY_REVEALED == m_gsi.trayVisState ) { + strId = R.string.board_menu_tray_hide; + } else { + strId = R.string.board_menu_tray_show; + } + item = menu.findItem( R.id.board_menu_tray ); + item.setTitle( strId ); + + Utils.setItemVisible( menu, R.id.board_menu_flip, + m_gsi.visTileCount >= 1 ); + Utils.setItemVisible( menu, R.id.board_menu_toggle, + m_gsi.visTileCount >= 1 ); + Utils.setItemVisible( menu, R.id.board_menu_juggle, + m_gsi.canShuffle ); + Utils.setItemVisible( menu, R.id.board_menu_undo_current, + m_gsi.canRedo ); + Utils.setItemVisible( menu, R.id.board_menu_hint_prev, + m_gsi.canHint ); + Utils.setItemVisible( menu, R.id.board_menu_hint_next, + m_gsi.canHint ); + Utils.setItemVisible( menu, R.id.board_menu_chat, + BuildConstants.CHAT_SUPPORTED + && m_gsi.canChat ); + Utils.setItemVisible( menu, R.id.board_menu_tray, + !inTrade && m_gsi.canHideRack ); + Utils.setItemVisible( menu, R.id.board_menu_trade, + m_gsi.canTrade ); + Utils.setItemVisible( menu, R.id.board_menu_undo_last, + m_gsi.canUndo ); + } + + Utils.setItemVisible( menu, R.id.board_menu_invite, 0 < m_missing ); + + Utils.setItemVisible( menu, R.id.board_menu_trade_cancel, inTrade ); + Utils.setItemVisible( menu, R.id.board_menu_trade_commit, + inTrade && m_gsi.tradeTilesSelected + && m_gsi.curTurnSelected ); + Utils.setItemVisible( menu, R.id.board_menu_game_resign, !inTrade ); + + if ( !inTrade ) { + boolean enabled = null == m_gsi || m_gsi.curTurnSelected; + item = menu.findItem( R.id.board_menu_done ); + item.setVisible( enabled ); + if ( enabled ) { + if ( 0 >= m_view.curPending() ) { + strId = R.string.board_menu_pass; + } else { + strId = R.string.board_menu_done; + } + item.setTitle( strId ); + } + if ( m_gameOver || DBUtils.gameOver( m_activity, m_rowid ) ) { + m_gameOver = true; + item = menu.findItem( R.id.board_menu_game_resign ); + item.setTitle( R.string.board_menu_game_final ); + } + } + + boolean enable = null != m_gi + && DeviceRole.SERVER_STANDALONE != m_gi.serverRole; + Utils.setItemVisible( menu, R.id.gamel_menu_checkmoves, enable ); + Utils.setItemVisible( menu, R.id.board_menu_game_resend, + enable && null != m_gsi && + 0 < m_gsi.nPendingMessages ); + + enable = enable && BuildConfig.DEBUG; + Utils.setItemVisible( menu, R.id.board_menu_game_netstats, enable ); + + enable = XWPrefs.getStudyEnabled( m_activity ); + Utils.setItemVisible( menu, R.id.games_menu_study, enable ); + + return true; + } // onPrepareOptionsMenu + + protected boolean onOptionsItemSelected( MenuItem item ) + { + boolean handled = true; + JNICmd cmd = JNICmd.CMD_NONE; + Runnable proc = null; + + int id = item.getItemId(); + switch ( id ) { + case R.id.board_menu_done: + int nTiles = XwJNI.model_getNumTilesInTray( m_jniGamePtr, + m_view.getCurPlayer() ); + if ( XWApp.MAX_TRAY_TILES > nTiles ) { + showNotAgainDlgThen( R.string.not_again_done, + R.string.key_notagain_done, + Action.COMMIT_ACTION ); + } else { + dlgButtonClicked( Action.COMMIT_ACTION, AlertDialog.BUTTON_POSITIVE, null ); + } + break; + + case R.id.board_menu_trade_commit: + cmd = JNICmd.CMD_COMMIT; + break; + case R.id.board_menu_trade_cancel: + cmd = JNICmd.CMD_CANCELTRADE; + break; + + case R.id.board_menu_hint_prev: + cmd = JNICmd.CMD_PREV_HINT; + break; + case R.id.board_menu_hint_next: + cmd = JNICmd.CMD_NEXT_HINT; + break; + case R.id.board_menu_juggle: + cmd = JNICmd.CMD_JUGGLE; + break; + case R.id.board_menu_flip: + cmd = JNICmd.CMD_FLIP; + break; + case R.id.board_menu_zoom: + cmd = JNICmd.CMD_TOGGLEZOOM; + break; + case R.id.board_menu_chat: + startChatActivity(); + break; + case R.id.board_menu_toggle: + cmd = JNICmd.CMD_VALUES; + break; + + case R.id.board_menu_trade: + String msg = m_activity.getString( R.string.not_again_trading ); + int strID = ABUtils.haveActionBar() ? R.string.not_again_trading_menu + : R.string. not_again_trading_buttons; + msg += m_activity.getString( strID ); + showNotAgainDlgThen( msg, R.string.key_notagain_trading, + Action.START_TRADE_ACTION ); + break; + + case R.id.board_menu_tray: + cmd = JNICmd.CMD_TOGGLE_TRAY; + break; + case R.id.games_menu_study: + StudyListActivity.launchOrAlert( m_activity, m_gi.dictLang, this ); + break; + case R.id.board_menu_game_netstats: + m_jniThread.handle( JNICmd.CMD_NETSTATS, R.string.netstats_title ); + break; + case R.id.board_menu_undo_current: + cmd = JNICmd.CMD_UNDO_CUR; + break; + case R.id.board_menu_undo_last: + showConfirmThen( R.string.confirm_undo_last, Action.UNDO_LAST_ACTION ); + break; + case R.id.board_menu_invite: + showDialog( DlgID.DLG_INVITE ); + break; + // small devices only + case R.id.board_menu_dict: + String dictName = m_gi.dictName( m_view.getCurPlayer() ); + DictBrowseActivity.launch( m_activity, dictName ); + break; + + case R.id.board_menu_game_counts: + m_jniThread.handle( JNICmd.CMD_COUNTS_VALUES, + R.string.counts_values_title ); + break; + case R.id.board_menu_game_left: + m_jniThread.handle( JNICmd.CMD_REMAINING, + R.string.tiles_left_title ); + break; + + case R.id.board_menu_game_history: + m_jniThread.handle( JNICmd.CMD_HISTORY, R.string.history_title ); + break; + + case R.id.board_menu_game_resign: + m_jniThread.handle( JNICmd.CMD_FINAL, R.string.history_title ); + break; + + case R.id.board_menu_game_resend: + m_jniThread.handle( JNICmd.CMD_RESEND, true, false ); + break; + + case R.id.gamel_menu_checkmoves: + showNotAgainDlgThen( R.string.not_again_sync, + R.string.key_notagain_sync, + Action.SYNC_ACTION ); + break; + + case R.id.board_menu_file_prefs: + m_firingPrefs = true; + Utils.launchSettings( m_activity ); + break; + + case R.id.board_menu_file_about: + showAboutDialog(); + break; + + default: + DbgUtils.logf( "menuitem %d not handled", id ); + handled = false; + } + + if ( handled && cmd != JNICmd.CMD_NONE ) { + m_jniThread.handle( cmd ); + } + return handled; + } + + ////////////////////////////////////////////////// + // DlgDelegate.DlgClickNotify interface + ////////////////////////////////////////////////// + @Override + public void dlgButtonClicked( Action action, int which, Object[] params ) + { + if ( Action.LAUNCH_INVITE_ACTION == action ) { + if ( DlgDelegate.DISMISS_BUTTON != which ) { + if ( DlgDelegate.NFC_BTN == which ) { + if ( NFCUtils.nfcAvail( m_activity )[1] ) { + showNotAgainDlgThen( R.string.not_again_sms_ready, + R.string.key_notagain_sms_ready ); + } else { + showDialog( DlgID.ENABLE_NFC ); + } + } else { + String inviteID = GameUtils.formatGameID( m_gi.gameID ); + GameUtils.launchInviteActivity( m_activity, which, m_room, + inviteID, m_gi.dictLang, + m_gi.dictName, + m_gi.nPlayers ); + } + } + } else if ( AlertDialog.BUTTON_POSITIVE == which ) { + JNICmd cmd = JNICmd.CMD_NONE; + switch ( action ) { + case UNDO_LAST_ACTION: + cmd = JNICmd.CMD_UNDO_LAST; + break; + case SYNC_ACTION: + doSyncMenuitem(); + break; + case BT_PICK_ACTION: + BTInviteActivity.launchForResult( m_activity, m_nMissingPlayers, + BT_INVITE_RESULT ); + break; + case SMS_PICK_ACTION: + SMSInviteActivity.launchForResult( m_activity, m_nMissingPlayers, + SMS_INVITE_RESULT ); + break; + case SMS_CONFIG_ACTION: + Utils.launchSettings( m_activity ); + break; + + case COMMIT_ACTION: + cmd = JNICmd.CMD_COMMIT; + break; + case SHOW_EXPL_ACTION: + Utils.showToast( m_activity, m_toastStr ); + m_toastStr = null; + break; + case BUTTON_BROWSEALL_ACTION: + case BUTTON_BROWSE_ACTION: + String curDict = m_gi.dictName( m_view.getCurPlayer() ); + View button = m_toolbar.getViewFor( Toolbar.BUTTON_BROWSE_DICT ); + if ( Action.BUTTON_BROWSEALL_ACTION == action && + DictsActivity.handleDictsPopup( m_activity, button, curDict ) ) { + break; + } + DictBrowseActivity.launch( m_activity, curDict ); + break; + case PREV_HINT_ACTION: + cmd = JNICmd.CMD_PREV_HINT; + break; + case NEXT_HINT_ACTION: + cmd = JNICmd.CMD_NEXT_HINT; + break; + case JUGGLE_ACTION: + cmd = JNICmd.CMD_JUGGLE; + break; + case FLIP_ACTION: + cmd = JNICmd.CMD_FLIP; + break; + case ZOOM_ACTION: + cmd = JNICmd.CMD_TOGGLEZOOM; + break; + case UNDO_ACTION: + cmd = JNICmd.CMD_UNDO_CUR; + break; + case VALUES_ACTION: + cmd = JNICmd.CMD_VALUES; + break; + case CHAT_ACTION: + startChatActivity(); + break; + case START_TRADE_ACTION: + Utils.showToast( m_activity, R.string.entering_trade ); + cmd = JNICmd.CMD_TRADE; + break; + case LOOKUP_ACTION: + launchLookup( m_words, m_gi.dictLang ); + break; + default: + Assert.fail(); + } + + if ( JNICmd.CMD_NONE != cmd ) { + checkAndHandle( cmd ); + } + } + } // dlgButtonClicked + + ////////////////////////////////////////////////// + // View.OnClickListener interface + ////////////////////////////////////////////////// + @Override + public void onClick( View view ) + { + if ( view == m_exchCommmitButton ) { + m_jniThread.handle( JNICmd.CMD_COMMIT ); + } else if ( view == m_exchCancelButton ) { + m_jniThread.handle( JNICmd.CMD_CANCELTRADE ); + } + } + + ////////////////////////////////////////////////// + // MultiService.MultiEventListener interface + ////////////////////////////////////////////////// + @Override + @SuppressWarnings("fallthrough") + public void eventOccurred( MultiService.MultiEvent event, final Object ... args ) + { + switch( event ) { + case MESSAGE_ACCEPTED: + case MESSAGE_REFUSED: + ConnStatusHandler. + updateStatusIn( m_activity, this, CommsConnType.COMMS_CONN_BT, + MultiService.MultiEvent.MESSAGE_ACCEPTED == event); + break; + case MESSAGE_NOGAME: + int gameID = (Integer)args[0]; + if ( gameID == m_gi.gameID ) { + post( new Runnable() { + public void run() { + showDialog( DlgID.DLG_DELETED ); + } + } ); + } + break; + + // This can be BT or SMS. In BT case there's a progress + // thing going. Not in SMS case. + case NEWGAME_FAILURE: + DbgUtils.logf( "failed to create game" ); + case NEWGAME_SUCCESS: + final boolean success = + MultiService.MultiEvent.NEWGAME_SUCCESS == event; + final boolean allHere = 0 == --m_invitesPending; + m_handler.post( new Runnable() { + public void run() { + if ( allHere ) { + stopProgress(); + } + if ( success ) { + DbgUtils.showf( m_activity, + R.string.invite_success ); + } + } + } ); + break; + + case SMS_SEND_OK: + ConnStatusHandler.showSuccessOut( this ); + break; + case SMS_RECEIVE_OK: + ConnStatusHandler.showSuccessIn( this ); + break; + case SMS_SEND_FAILED: + case SMS_SEND_FAILED_NORADIO: + + // if ( null != m_jniThread ) { + // boolean accepted = + // MultiService.MultiEvent.SMS_RECEIVE_OK == event + // || MultiService.MultiEvent.SMS_SEND_OK == event; + // m_jniThread.handle( JNICmd.CMD_DRAW_SMS_STATUS, accepted ); + // } + break; + + default: + super.eventOccurred( event, args ); + break; + } + } + + ////////////////////////////////////////////////// + // TransportProcs.TPMsgHandler interface + ////////////////////////////////////////////////// + + public void tpmRelayConnd( final String room, final int devOrder, + final boolean allHere, final int nMissing ) + { + post( new Runnable() { + public void run() { + handleConndMessage( room, devOrder, allHere, nMissing ); + } + } ); + } + + public void tpmRelayErrorProc( TransportProcs.XWRELAY_ERROR relayErr ) + { + int strID = -1; + DlgID dlgID = DlgID.NONE; + boolean doToast = false; + + switch ( relayErr ) { + case TOO_MANY: + strID = R.string.msg_too_many; + dlgID = DlgID.DLG_OKONLY; + break; + case NO_ROOM: + strID = R.string.msg_no_room; + dlgID = DlgID.DLG_RETRY; + break; + case DUP_ROOM: + strID = R.string.msg_dup_room; + dlgID = DlgID.DLG_OKONLY; + break; + case LOST_OTHER: + case OTHER_DISCON: + strID = R.string.msg_lost_other; + doToast = true; + break; + + case DEADGAME: + case DELETED: + strID = R.string.msg_dev_deleted; + dlgID = DlgID.DLG_DELETED; + break; + + case OLDFLAGS: + case BADPROTO: + case RELAYBUSY: + case SHUTDOWN: + case TIMEOUT: + case HEART_YOU: + case HEART_OTHER: + break; + } + + if ( doToast ) { + Utils.showToast( m_activity, strID ); + } else if ( dlgID != DlgID.NONE ) { + final int strIDf = strID; + final DlgID dlgIDf = dlgID; + post( new Runnable() { + public void run() { + m_dlgBytes = m_activity.getString( strIDf ); + m_dlgTitle = R.string.relay_alert; + showDialog( dlgIDf ); + } + }); + } + } + + ////////////////////////////////////////////////// + // DictImportActivity.DownloadFinishedListener interface + ////////////////////////////////////////////////// + public void downloadFinished( final String name, final boolean success ) + { + if ( success ) { + post( new Runnable() { + public void run() { + setGotGameDict( name ); + } + } ); + } + } + + ////////////////////////////////////////////////// + // NFCUtils.NFCActor + ////////////////////////////////////////////////// + + public String makeNFCMessage() + { + String data = null; + if ( 0 < m_missing ) { // Isn't there a better test?? + String inviteID = String.format( "%X", m_gi.gameID ); + String room = m_summary.roomName; + Assert.assertNotNull( room ); + data = NetLaunchInfo.makeLaunchJSON( m_activity, room, inviteID, + m_gi.dictLang, + m_gi.dictName, m_gi.nPlayers ); + } + return data; + } + + ////////////////////////////////////////////////// + // ConnStatusHandler.ConnStatusCBacks + ////////////////////////////////////////////////// + public void invalidateParent() + { + m_activity.runOnUiThread(new Runnable() { + public void run() { + m_view.invalidate(); + } + }); + } + + public void onStatusClicked() + { + final String msg = ConnStatusHandler.getStatusText( m_activity, m_connType ); + post( new Runnable() { + public void run() { + m_dlgBytes = msg; + m_dlgTitle = R.string.info_title; + showDialog( DlgID.DLG_CONNSTAT ); + } + } ); + } + + public Handler getHandler() + { + return m_handler; + } + + private void setGotGameDict( String getDict ) + { + m_jniThread.setSaveDict( getDict ); + + String msg = m_activity.getString( R.string.reload_new_dict, getDict ); + Utils.showToast( m_activity, msg ); + m_activity.finish(); + GameUtils.launchGame( m_activity, m_rowid, false ); + } + + private XwJNI.XP_Key keyCodeToXPKey( int keyCode ) + { + XwJNI.XP_Key xpKey = XwJNI.XP_Key.XP_KEY_NONE; + switch( keyCode ) { + case KeyEvent.KEYCODE_DPAD_CENTER: + case KeyEvent.KEYCODE_ENTER: + xpKey = XwJNI.XP_Key.XP_RETURN_KEY; + break; + case KeyEvent.KEYCODE_DPAD_DOWN: + xpKey = XwJNI.XP_Key.XP_CURSOR_KEY_DOWN; + break; + case KeyEvent.KEYCODE_DPAD_LEFT: + xpKey = XwJNI.XP_Key.XP_CURSOR_KEY_LEFT; + break; + case KeyEvent.KEYCODE_DPAD_RIGHT: + xpKey = XwJNI.XP_Key.XP_CURSOR_KEY_RIGHT; + break; + case KeyEvent.KEYCODE_DPAD_UP: + xpKey = XwJNI.XP_Key.XP_CURSOR_KEY_UP; + break; + case KeyEvent.KEYCODE_SPACE: + xpKey = XwJNI.XP_Key.XP_RAISEFOCUS_KEY; + break; + } + return xpKey; + } + + // Blocking thread stuff: The problem this is solving occurs when + // you have a blocking dialog up, meaning the jni thread is + // blocked, and you hit the home button. onPause() gets called + // which wants to use jni calls to e.g. summarize. For those to + // succeed (the jni being non-reentrant and set up to assert if it + // is reentered) the jni thread must first be unblocked and + // allowed to return back through the jni. We unblock using + // Thread.interrupt method, the exception from which winds up + // caught in waitBlockingDialog. The catch dismisses the dialog + // with the default/cancel value, but that takes us into the + // onDismissListener which normally releases the semaphore. But + // if we've interrupted then we can't release it or blocking won't + // work for as long as this activity lives. Hence + // releaseIfBlocking(). This feels really fragile but it does + // work. + private void setBlockingThread() + { + synchronized( this ) { + Assert.assertTrue( null == m_blockingThread ); + m_blockingThread = Thread.currentThread(); + } + } + + private void clearBlockingThread() + { + synchronized( this ) { + Assert.assertTrue( null != m_blockingThread ); + m_blockingThread = null; + } + } + + private void interruptBlockingThread() + { + synchronized( this ) { + if ( null != m_blockingThread ) { + m_blockingThread.interrupt(); + } + } + } + + private void releaseIfBlocking() + { + synchronized( this ) { + if ( null != m_blockingThread ) { + m_forResultWait.release(); + } + } + } + + private void handleConndMessage( String room, int devOrder, // <- hostID + boolean allHere, int nMissing ) + { + int naMsg = 0; + int naKey = 0; + String toastStr = null; + if ( allHere ) { + // All players have now joined the game. The device that + // created the room will assign tiles. Then it will be + // the first player's turn + + // Skip this until there's a way to show it only once per game + if ( false ) { + toastStr = m_activity.getString( R.string.msg_relay_all_heref, room ); + if ( devOrder > 1 ) { + naMsg = R.string.not_again_conndall; + naKey = R.string.key_notagain_conndall; + } + } + } else if ( nMissing > 0 ) { + if ( !m_haveInvited ) { + m_haveInvited = true; + m_room = room; + m_missing = nMissing; + showDialog( DlgID.DLG_INVITE ); + ABUtils.invalidateOptionsMenuIf( m_activity ); + } else { + toastStr = m_activity.getString( R.string.msg_relay_waiting, devOrder, + room, nMissing ); + if ( devOrder == 1 ) { + naMsg = R.string.not_again_conndfirst; + naKey = R.string.key_notagain_conndfirst; + } else { + naMsg = R.string.not_again_conndmid; + naKey = R.string.key_notagain_conndmid; + } + } + } + + if ( null != toastStr ) { + m_toastStr = toastStr; + if ( naMsg == 0 ) { + dlgButtonClicked( Action.SHOW_EXPL_ACTION, + AlertDialog.BUTTON_POSITIVE, null ); + } else { + showNotAgainDlgThen( naMsg, naKey, Action.SHOW_EXPL_ACTION ); + } + } + + m_missing = nMissing; + ABUtils.invalidateOptionsMenuIf( m_activity ); + } // handleConndMessage + + private class BoardUtilCtxt extends UtilCtxtImpl { + + public BoardUtilCtxt() + { + super( m_activity ); + } + + @Override + public void requestTime() + { + post( new Runnable() { + public void run() { + if ( null != m_jniThread ) { + m_jniThread.handleBkgrnd( JNICmd.CMD_DO ); + } + } + } ); + } + + @Override + public void remSelected() + { + m_jniThread.handle( JNICmd.CMD_REMAINING, + R.string.tiles_left_title ); + } + + @Override + public void setIsServer( boolean isServer ) + { + DeviceRole newRole = isServer? DeviceRole.SERVER_ISSERVER + : DeviceRole.SERVER_ISCLIENT; + if ( newRole != m_gi.serverRole ) { + m_gi.serverRole = newRole; + if ( !isServer ) { + m_jniThread.handle( JNICmd.CMD_SWITCHCLIENT ); + } + } + } + + @Override + public void bonusSquareHeld( int bonus ) + { + int id = 0; + switch( bonus ) { + case BONUS_DOUBLE_LETTER: + id = R.string.bonus_l2x; + break; + case BONUS_DOUBLE_WORD: + id = R.string.bonus_w2x; + break; + case BONUS_TRIPLE_LETTER: + id = R.string.bonus_l3x; + break; + case BONUS_TRIPLE_WORD: + id = R.string.bonus_w3x; + break; + default: + Assert.fail(); + } + + if ( 0 != id ) { + final String bonusStr = m_activity.getString( id ); + post( new Runnable() { + public void run() { + Utils.showToast( m_activity, bonusStr ); + } + } ); + } + } + + @Override + public void playerScoreHeld( int player ) + { + String expl = XwJNI.model_getPlayersLastScore( m_jniGamePtr, + player ); + if ( expl.length() == 0 ) { + expl = m_activity.getString( R.string.no_moves_made ); + } + String name = m_gi.players[player].name; + final String text = String.format( "%s\n%s", name, expl ); + post( new Runnable() { + public void run() { + Utils.showToast( m_activity, text ); + } + } ); + } + + @Override + public void cellSquareHeld( final String words ) + { + post( new Runnable() { + public void run() { + launchLookup( wordsToArray( words ), m_gi.dictLang ); + } + } ); + } + + @Override + public void setTimer( int why, int when, int handle ) + { + if ( null != m_timers[why] ) { + removeCallbacks( m_timers[why] ); + } + + m_timers[why] = new TimerRunnable( why, when, handle ); + + int inHowLong; + switch ( why ) { + case UtilCtxt.TIMER_COMMS: + inHowLong = when * 1000; + break; + case UtilCtxt.TIMER_TIMERTICK: + inHowLong = 1000; // when is 0 for TIMER_TIMERTICK + break; + default: + inHowLong = 500; + } + postDelayed( m_timers[why], inHowLong ); + } + + @Override + public void clearTimer( int why ) + { + if ( null != m_timers[why] ) { + removeCallbacks( m_timers[why] ); + m_timers[why] = null; + } + } + + // This is supposed to be called from the jni thread + @Override + public int userPickTileBlank( int playerNum, String[] texts) + { + m_texts = texts; + waitBlockingDialog( DlgID.PICK_TILE_REQUESTBLANK_BLK, 0 ); + return m_resultCode; + } + + @Override + public int userPickTileTray( int playerNum, String[] texts, + String[] curTiles, int nPicked ) + { + m_texts = texts; + m_curTiles = TextUtils.join( ", ", curTiles ); + m_canUndoTiles = 0 < nPicked; + waitBlockingDialog( DlgID.PICK_TILE_REQUESTTRAY_BLK, + UtilCtxt.PICKER_PICKALL ); + return m_resultCode; + } + + @Override + public String askPassword( String name ) + { + // call this each time dlg created or will get exception + // for reusing m_passwdLyt + m_pwdName = name; + setupPasswdVars(); + + waitBlockingDialog( DlgID.ASK_PASSWORD_BLK, 0 ); + + String result = null; // means cancelled + if ( 0 != m_resultCode ) { + result = m_passwdEdit.getText().toString(); + } + return result; + } + + @Override + public void turnChanged( int newTurn ) + { + if ( 0 <= newTurn ) { + post( new Runnable() { + public void run() { + showNotAgainDlgThen( R.string.not_again_turnchanged, + R.string.key_notagain_turnchanged ); + } + } ); + m_jniThread.handle( JNICmd. CMD_ZOOM, -8 ); + } + } + + @Override + public boolean engineProgressCallback() + { + return ! m_jniThread.busy(); + } + + @Override + public boolean userQuery( int id, String query ) + { + boolean result; + + switch( id ) { + // Though robot-move dialogs don't normally need to block, + // if the player after this one is also a robot and we + // don't block then a second dialog will replace this one. + // So block. Yuck. + case UtilCtxt.QUERY_ROBOT_TRADE: + m_dlgBytes = query; + m_dlgTitle = R.string.info_title; + waitBlockingDialog( DlgID.QUERY_INFORM_BLK, 0 ); + result = true; + break; + + // These *are* blocking dialogs + case UtilCtxt.QUERY_COMMIT_TURN: + m_dlgBytes = query; + m_dlgTitle = R.string.query_title; + result = 0 != waitBlockingDialog( DlgID.QUERY_REQUEST_BLK, 0 ); + break; + default: + Assert.fail(); + result = false; + } + + return result; + } + + @Override + public boolean confirmTrade( String[] tiles ) + { + m_dlgTitle = R.string.info_title; + m_dlgBytes = m_activity.getString( R.string.query_tradef, + TextUtils.join( ", ", tiles ) ); + return 0 != waitBlockingDialog( DlgID.QUERY_REQUEST_BLK, 0 ); + } + + @Override + public void userError( int code ) + { + int resid = 0; + switch( code ) { + case UtilCtxt.ERR_TILES_NOT_IN_LINE: + resid = R.string.str_tiles_not_in_line; + break; + case UtilCtxt.ERR_NO_EMPTIES_IN_TURN: + resid = R.string.str_no_empties_in_turn; + break; + case UtilCtxt.ERR_TWO_TILES_FIRST_MOVE: + resid = R.string.str_two_tiles_first_move; + break; + case UtilCtxt.ERR_TILES_MUST_CONTACT: + resid = R.string.str_tiles_must_contact; + break; + case UtilCtxt.ERR_NOT_YOUR_TURN: + resid = R.string.str_not_your_turn; + break; + case UtilCtxt.ERR_NO_PEEK_ROBOT_TILES: + resid = R.string.str_no_peek_robot_tiles; + break; + case UtilCtxt.ERR_NO_EMPTY_TRADE: + // This should not be possible as the button's + // disabled when no tiles selected. + Assert.fail(); + break; + case UtilCtxt.ERR_TOO_FEW_TILES_LEFT_TO_TRADE: + resid = R.string.str_too_few_tiles_left_to_trade; + break; + case UtilCtxt.ERR_CANT_UNDO_TILEASSIGN: + resid = R.string.str_cant_undo_tileassign; + break; + case UtilCtxt.ERR_CANT_HINT_WHILE_DISABLED: + resid = R.string.str_cant_hint_while_disabled; + break; + case UtilCtxt.ERR_NO_PEEK_REMOTE_TILES: + resid = R.string.str_no_peek_remote_tiles; + break; + case UtilCtxt.ERR_REG_UNEXPECTED_USER: + resid = R.string.str_reg_unexpected_user; + break; + case UtilCtxt.ERR_SERVER_DICT_WINS: + resid = R.string.str_server_dict_wins; + break; + case ERR_REG_SERVER_SANS_REMOTE: + resid = R.string.str_reg_server_sans_remote; + break; + } + + if ( resid != 0 ) { + nonBlockingDialog( DlgID.DLG_OKONLY, m_activity.getString( resid ) ); + } + } // userError + + @Override + public void informMissing( boolean isServer, CommsConnType connType, + final int nMissingPlayers ) + { + m_connType = connType; + + Action action = null; + if ( 0 < nMissingPlayers && isServer && !m_haveInvited ) { + switch( connType ) { + case COMMS_CONN_BT: + action = Action.BT_PICK_ACTION; + break; + case COMMS_CONN_SMS: + action = Action.SMS_PICK_ACTION; + break; + } + } + if ( null != action ) { + m_haveInvited = true; + final Action faction = action; + final String fmsg = m_activity.getString( R.string.invite_msgf, + nMissingPlayers ); + post( new Runnable() { + public void run() { + DbgUtils.showf( m_activity, + m_activity.getString( R.string.players_missf, + nMissingPlayers ) ); + m_nMissingPlayers = nMissingPlayers; + showConfirmThen( fmsg, faction ); + } + } ); + } + } + + @Override + public void informMove( String expl, String words ) + { + m_words = null == words? null : wordsToArray( words ); + nonBlockingDialog( DlgID.DLG_SCORES, expl ); + } + + @Override + public void informUndo() + { + nonBlockingDialog( DlgID.DLG_OKONLY, + m_activity.getString( R.string.remote_undone ) ); + } + + @Override + public void informNetDict( int code, String oldName, + String newName, String newSum, + CurGameInfo.XWPhoniesChoice phonies ) + { + // If it's same dict and same sum, we're good. That + // should be the normal case. Otherwise: if same name but + // different sum, notify and offer to upgrade. If + // different name, offer to install. + String msg = null; + if ( oldName.equals( newName ) ) { + String oldSum = DictLangCache.getDictMD5Sum( m_activity, + oldName ); + if ( !oldSum.equals( newSum ) ) { + // Same dict, different versions + msg = m_activity.getString( R.string.inform_dict_diffversionf, + oldName ); + } + } else { + // Different dict! If we have the other one, switch + // to it. Otherwise offer to download + DlgID dlgID; + msg = m_activity.getString( R.string.inform_dict_diffdictf, + oldName, newName, newName ); + if ( DictLangCache.haveDict( m_activity, code, + newName ) ) { + dlgID = DlgID.DLG_USEDICT; + } else { + dlgID = DlgID.DLG_GETDICT; + msg += m_activity.getString( R.string.inform_dict_download ); + } + m_getDict = newName; + nonBlockingDialog( dlgID, msg ); + } + } + + @Override + public void notifyGameOver() + { + m_gameOver = true; + m_jniThread.handle( JNICmd.CMD_POST_OVER ); + } + + // public void yOffsetChange( int maxOffset, int oldOffset, int newOffset ) + // { + // DbgUtils.logf( "yOffsetChange(maxOffset=%d)", maxOffset ); + // m_view.setVerticalScrollBarEnabled( maxOffset > 0 ); + // } + @Override + public boolean warnIllegalWord( String dict, String[] words, int turn, + boolean turnLost ) + { + boolean accept = turnLost; + + String wordsString = TextUtils.join( ", ", words ); + String message = + m_activity.getString( R.string.ids_badwordsf, wordsString, dict ); + + if ( turnLost ) { + m_dlgBytes = message + m_activity.getString( R.string.badwords_lost ); + m_dlgTitle = R.string.badwords_title; + waitBlockingDialog( DlgID.DLG_BADWORDS_BLK, 0 ); + } else { + m_dlgBytes = message + m_activity.getString( R.string.badwords_accept ); + m_dlgTitle = R.string.query_title; + accept = 0 != waitBlockingDialog( DlgID.QUERY_REQUEST_BLK, 0 ); + } + + return accept; + } + + // Let's have this block in case there are multiple messages. If + // we don't block the jni thread will continue processing messages + // and may stack dialogs on top of this one. Including later + // chat-messages. + @Override + public void showChat( final String msg ) + { + if ( BuildConstants.CHAT_SUPPORTED ) { + post( new Runnable() { + public void run() { + DBUtils.appendChatHistory( m_activity, + m_rowid, msg, false ); + startChatActivity(); + } + } ); + } + } + } // class BoardUtilCtxt + + private void loadGame() + { + if ( 0 == m_jniGamePtr ) { + try { + String[] dictNames = GameUtils.dictNames( m_activity, m_rowid ); + DictUtils.DictPairs pairs = DictUtils.openDicts( m_activity, dictNames ); + + if ( pairs.anyMissing( dictNames ) ) { + showDictGoneFinish(); + } else { + Assert.assertNull( m_gameLock ); + m_gameLock = new GameLock( m_rowid, true ).lock(); + + byte[] stream = GameUtils.savedGame( m_activity, m_gameLock ); + m_gi = new CurGameInfo( m_activity ); + XwJNI.gi_from_stream( m_gi, stream ); + String langName = m_gi.langName(); + + setThis( this ); + + m_jniGamePtr = XwJNI.initJNI(); + + if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) { + m_xport = new CommsTransport( m_jniGamePtr, m_activity, this, + m_rowid, m_gi.serverRole ); + } + + CommonPrefs cp = CommonPrefs.get( m_activity ); + if ( null == stream || + ! XwJNI.game_makeFromStream( m_jniGamePtr, stream, + m_gi, dictNames, + pairs.m_bytes, + pairs.m_paths, langName, + m_utils, m_jniu, + null, cp, m_xport ) ) { + XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, + m_jniu, null, cp, m_xport, + dictNames, pairs.m_bytes, + pairs.m_paths, langName ); + } + + m_summary = new GameSummary( m_activity, m_gi ); + XwJNI.game_summarize( m_jniGamePtr, m_summary ); + + Handler handler = new Handler() { + public void handleMessage( Message msg ) { + switch( msg.what ) { + case JNIThread.DIALOG: + m_dlgBytes = (String)msg.obj; + m_dlgTitle = msg.arg1; + showDialog( DlgID.DLG_OKONLY ); + break; + case JNIThread.QUERY_ENDGAME: + showDialog( DlgID.QUERY_ENDGAME ); + break; + case JNIThread.TOOLBAR_STATES: + if ( null != m_jniThread ) { + m_gsi = + m_jniThread.getGameStateInfo(); + updateToolbar(); + if ( m_inTrade != m_gsi.inTrade ) { + m_inTrade = m_gsi.inTrade; + } + m_view.setInTrade( m_inTrade ); + adjustTradeVisibility(); + ABUtils.invalidateOptionsMenuIf( m_activity ); + } + break; + case JNIThread.GOT_WORDS: + launchLookup( wordsToArray((String)msg.obj), + m_gi.dictLang ); + break; + case JNIThread.GAME_OVER: + m_dlgBytes = (String)msg.obj; + m_dlgTitle = msg.arg1; + showDialog( DlgID.GAME_OVER ); + break; + } + } + }; + m_jniThread = + new JNIThread( m_jniGamePtr, stream, m_gi, + m_view, m_gameLock, m_activity, handler ); + // see http://stackoverflow.com/questions/680180/where-to-stop-\ + // destroy-threads-in-android-service-class + m_jniThread.setDaemon( true ); + m_jniThread.start(); + + m_view.startHandling( m_activity, m_jniThread, m_jniGamePtr, m_gi, + m_connType ); + if ( null != m_xport ) { + m_xport.setReceiver( m_jniThread, m_handler ); + } + m_jniThread.handle( JNICmd.CMD_START ); + + if ( !CommonPrefs.getHideTitleBar( m_activity ) ) { + m_activity.setTitle( GameUtils.getName( m_activity, m_rowid ) ); + } + m_toolbar = new Toolbar( m_activity, this, R.id.toolbar_horizontal ); + + populateToolbar(); + adjustTradeVisibility(); + + int flags = DBUtils.getMsgFlags( m_activity, m_rowid ); + if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) { + startChatActivity(); + } + if ( m_overNotShown ) { + boolean auto = false; + if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) { + m_gameOver = true; + } else if ( DBUtils.gameOver( m_activity, m_rowid ) ) { + m_gameOver = true; + auto = true; + } + if ( m_gameOver ) { + m_overNotShown = false; + m_jniThread.handle( JNICmd.CMD_POST_OVER, auto ); + } + } + if ( 0 != flags ) { + DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE ); + } + + if ( null != m_xport ) { + warnIfNoTransport(); + trySendChats(); + Utils.cancelNotification( m_activity, (int)m_rowid ); + m_xport.tickle( m_connType ); + tryInvites(); + } + } + } catch ( GameUtils.NoSuchGameException nsge ) { + DbgUtils.loge( nsge ); + m_activity.finish(); + } + } + } // loadGame + + private void checkAndHandle( JNICmd cmd ) + { + if ( null != m_jniThread ) { + m_jniThread.handle( cmd ); + } + } + + private void populateToolbar() + { + m_toolbar.setListener( Toolbar.BUTTON_BROWSE_DICT, + R.string.not_again_browseall, + R.string.key_na_browseall, + Action.BUTTON_BROWSEALL_ACTION ); + m_toolbar.setLongClickListener( Toolbar.BUTTON_BROWSE_DICT, + R.string.not_again_browse, + R.string.key_na_browse, + Action.BUTTON_BROWSE_ACTION ); + m_toolbar.setListener( Toolbar.BUTTON_HINT_PREV, + R.string.not_again_hintprev, + R.string.key_notagain_hintprev, + Action.PREV_HINT_ACTION ); + m_toolbar.setListener( Toolbar.BUTTON_HINT_NEXT, + R.string.not_again_hintnext, + R.string.key_notagain_hintnext, + Action.NEXT_HINT_ACTION ); + m_toolbar.setListener( Toolbar.BUTTON_JUGGLE, + R.string.not_again_juggle, + R.string.key_notagain_juggle, + Action.JUGGLE_ACTION ); + m_toolbar.setListener( Toolbar.BUTTON_FLIP, + R.string.not_again_flip, + R.string.key_notagain_flip, + Action.FLIP_ACTION ); + m_toolbar.setListener( Toolbar.BUTTON_ZOOM, + R.string.not_again_zoom, + R.string.key_notagain_zoom, + Action.ZOOM_ACTION ); + m_toolbar.setListener( Toolbar.BUTTON_VALUES, + R.string.not_again_values, + R.string.key_na_values, + Action.VALUES_ACTION ); + m_toolbar.setListener( Toolbar.BUTTON_UNDO, + R.string.not_again_undo, + R.string.key_notagain_undo, + Action.UNDO_ACTION ); + if ( BuildConstants.CHAT_SUPPORTED ) { + m_toolbar.setListener( Toolbar.BUTTON_CHAT, + R.string.not_again_chat, + R.string.key_notagain_chat, + Action.CHAT_ACTION ); + } + } // populateToolbar + + private OnDismissListener makeODLforBlocking( final int id ) + { + return new OnDismissListener() { + public void onDismiss( DialogInterface di ) { + releaseIfBlocking(); + m_activity.removeDialog( id ); + } + }; + } + + private int waitBlockingDialog( final DlgID dlgID, int cancelResult ) + { + int result = cancelResult; + // this has been true; dunno why + if ( DlgID.NONE != m_blockingDlgID ) { + DbgUtils.logf( "waitBlockingDialog: dropping dlgID %d b/c %d set", + dlgID, m_blockingDlgID ); + } else { + setBlockingThread(); + m_resultCode = cancelResult; + + if ( post( new Runnable() { + public void run() { + m_blockingDlgID = dlgID; + showDialog( dlgID ); + } + } ) ) { + + try { + m_forResultWait.acquire(); + } catch ( java.lang.InterruptedException ie ) { + DbgUtils.loge( ie ); + if ( DlgID.NONE != m_blockingDlgID ) { + try { + m_activity.dismissDialog( m_blockingDlgID.ordinal() ); + } catch ( java.lang.IllegalArgumentException iae ) { + DbgUtils.loge( iae ); + } + } + } + m_blockingDlgID = DlgID.NONE; + } + + clearBlockingThread(); + result = m_resultCode; + } + return result; + } + + private void nonBlockingDialog( final DlgID dlgID, String txt ) + { + switch ( dlgID ) { + case DLG_OKONLY: + case DLG_SCORES: + m_dlgTitle = R.string.info_title; + break; + case DLG_USEDICT: + case DLG_GETDICT: + m_dlgTitle = R.string.inform_dict_title; + break; + + default: + Assert.fail(); + } + + m_dlgBytes = txt; + post( new Runnable() { + public void run() { + showDialog( dlgID ); + } + } ); + } + + private boolean doZoom( int zoomBy ) + { + boolean handled = null != m_jniThread; + if ( handled ) { + m_jniThread.handle( JNICmd.CMD_ZOOM, zoomBy ); + } + return handled; + } + + private void startChatActivity() + { + if ( BuildConstants.CHAT_SUPPORTED ) { + Intent intent = new Intent( m_activity, ChatActivity.class ); + intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_rowid ); + m_activity.startActivityForResult( intent, CHAT_REQUEST ); + } + } + + private void waitCloseGame( boolean save ) + { + if ( 0 != m_jniGamePtr ) { + if ( null != m_xport ) { + m_xport.waitToStop(); + m_xport = null; + } + + interruptBlockingThread(); + + if ( null != m_jniThread ) { + m_jniThread.waitToStop( save ); + m_jniThread = null; + } + m_view.stopHandling(); + + clearThis( this ); + + if ( XWPrefs.getThumbEnabled( m_activity ) ) { + // Before we dispose, and after JNIThread has + // relinquished interest, redraw on smaller scale. + Bitmap thumb = + GameUtils.takeSnapshot( m_activity, m_jniGamePtr, m_gi ); + DBUtils.saveThumbnail( m_activity, m_gameLock, thumb ); + } + + XwJNI.game_dispose( m_jniGamePtr ); + m_jniGamePtr = 0; + m_gi = null; + + m_gameLock.unlock(); + m_gameLock = null; + } + } + + private void warnIfNoTransport() + { + switch( m_connType ) { + case COMMS_CONN_SMS: + if ( XWApp.SMSSUPPORTED && !XWPrefs.getSMSEnabled( m_activity ) ) { + showConfirmThen( R.string.warn_sms_disabled, + R.string.newgame_enable_sms, + Action.SMS_CONFIG_ACTION ); + } + break; + } + } + + private void trySendChats() + { + if ( BuildConstants.CHAT_SUPPORTED && null != m_jniThread ) { + Iterator iter = m_pendingChats.iterator(); + while ( iter.hasNext() ) { + m_jniThread.handle( JNICmd.CMD_SENDCHAT, iter.next() ); + } + m_pendingChats.clear(); + } + } + + private void tryInvites() + { + if ( XWApp.BTSUPPORTED || XWApp.SMSSUPPORTED ) { + if ( null != m_missingDevs ) { + String gameName = GameUtils.getName( m_activity, m_rowid ); + boolean doProgress = false; + m_invitesPending = m_missingDevs.length; + for ( String dev : m_missingDevs ) { + switch( m_connType ) { + case COMMS_CONN_BT: + BTService.inviteRemote( m_activity, dev, m_gi.gameID, + gameName, m_gi.dictLang, + m_gi.dictName, m_gi.nPlayers, + 1 ); + break; + case COMMS_CONN_SMS: + SMSService.inviteRemote( m_activity, dev, m_gi.gameID, + gameName, m_gi.dictLang, + m_gi.dictName, m_gi.nPlayers, + 1 ); + break; + } + } + if ( doProgress ) { + startProgress( R.string.invite_progress ); + } + m_missingDevs = null; + } + } + } + + private void updateToolbar() + { + m_toolbar.update( Toolbar.BUTTON_FLIP, m_gsi.visTileCount >= 1 ); + m_toolbar.update( Toolbar.BUTTON_VALUES, m_gsi.visTileCount >= 1 ); + m_toolbar.update( Toolbar.BUTTON_JUGGLE, m_gsi.canShuffle ); + m_toolbar.update( Toolbar.BUTTON_UNDO, m_gsi.canRedo ); + m_toolbar.update( Toolbar.BUTTON_HINT_PREV, m_gsi.canHint ); + m_toolbar.update( Toolbar.BUTTON_HINT_NEXT, m_gsi.canHint ); + m_toolbar.update( Toolbar.BUTTON_CHAT, + BuildConstants.CHAT_SUPPORTED && m_gsi.canChat ); + m_toolbar.update( Toolbar.BUTTON_BROWSE_DICT, + null != m_gi.dictName( m_view.getCurPlayer() ) ); + } + + private void adjustTradeVisibility() + { + m_toolbar.setVisibility( m_inTrade? View.GONE : View.VISIBLE ); + if ( null != m_tradeButtons ) { + m_tradeButtons.setVisibility( m_inTrade? View.VISIBLE : View.GONE ); + } + if ( m_inTrade && null != m_exchCommmitButton ) { + m_exchCommmitButton.setEnabled( m_gsi.tradeTilesSelected ); + } + } + + private void setBackgroundColor() + { + View view = m_activity.findViewById( R.id.board_root ); + // Google's reported an NPE here, so test + if ( null != view ) { + int back = CommonPrefs.get( m_activity ) + .otherColors[CommonPrefs.COLOR_BACKGRND]; + view.setBackgroundColor( back ); + } + } + + private void setKeepScreenOn() + { + boolean keepOn = CommonPrefs.getKeepScreenOn( m_activity ); + m_view.setKeepScreenOn( keepOn ); + + if ( keepOn ) { + if ( null == m_screenTimer ) { + m_screenTimer = new Runnable() { + public void run() { + if ( null != m_view ) { + m_view.setKeepScreenOn( false ); + } + } + }; + } + removeCallbacks( m_screenTimer ); // needed? + postDelayed( m_screenTimer, SCREEN_ON_TIME ); + } + } + + @Override + protected boolean post( Runnable runnable ) + { + boolean canPost = null != m_handler; + if ( canPost ) { + m_handler.post( runnable ); + } else { + DbgUtils.logf( "post: dropping because handler null" ); + } + return canPost; + } + + private void postDelayed( Runnable runnable, int when ) + { + if ( null != m_handler ) { + m_handler.postDelayed( runnable, when ); + } else { + DbgUtils.logf( "postDelayed: dropping %d because handler null", when ); + } + } + + private void removeCallbacks( Runnable which ) + { + if ( null != m_handler ) { + m_handler.removeCallbacks( which ); + } else { + DbgUtils.logf( "removeCallbacks: dropping %h because handler null", + which ); + } + } + + private String[] wordsToArray( String words ) + { + String[] tmp = TextUtils.split( words, "\n" ); + String[] wordsArray = new String[tmp.length]; + for ( int ii = 0, jj = tmp.length; ii < tmp.length; ++ii, --jj ) { + wordsArray[ii] = tmp[jj-1]; + } + return wordsArray; + } + + private void setupPasswdVars() + { + m_dlgTitleStr = m_activity.getString( R.string.msg_ask_password, m_pwdName ); + m_passwdLyt = (LinearLayout)Utils.inflate( m_activity, + R.layout.passwd_view ); + m_passwdEdit = (EditText)m_passwdLyt.findViewById( R.id.edit ); + } + + private void doRematch() + { + Intent intent = GamesListActivity.makeRematchIntent( m_activity, m_gi, m_rowid ); + if ( null != intent ) { + m_activity.startActivity( intent ); + m_activity.finish(); + } + } + + private Button setListenerOrHide( int id ) + { + Button button = (Button)m_activity.findViewById( id ); + if ( null != button ) { + if ( ABUtils.haveActionBar() ) { + button.setVisibility( View.INVISIBLE ); + } else { + button.setOnClickListener( this ); + } + } + return button; + } + + private static void noteSkip() + { + String msg = "BoardActivity.feedMessage[s](): skipped because " + + "too many open Boards"; + DbgUtils.logf(msg ); + } +} // class BoardActivity 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 b3946c4d3..a97962f24 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardView.java @@ -20,6 +20,7 @@ package org.eehouse.android.xw4; +import android.app.Activity; import android.view.View; import android.graphics.Canvas; import android.graphics.Paint; @@ -58,7 +59,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw { private int m_layoutHeight; private BoardCanvas m_canvas; // owns the bitmap private JNIThread m_jniThread; - private XWActivity m_parent; + private Activity m_parent; private boolean m_measuredFromDims = false; private BoardDims m_dims; private CommsAddrRec.CommsConnType m_connType = @@ -257,7 +258,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw { } // layoutBoardOnce // BoardHandler interface implementation - public void startHandling( XWActivity parent, JNIThread thread, + public void startHandling( Activity parent, JNIThread thread, int gamePtr, CurGameInfo gi, CommsAddrRec.CommsConnType connType ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DelegateBase.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DelegateBase.java index 570abd275..8b3063dbd 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DelegateBase.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DelegateBase.java @@ -27,7 +27,9 @@ import org.eehouse.android.xw4.DlgDelegate.Action; import junit.framework.Assert; -public class DelegateBase implements DlgDelegate.DlgClickNotify { +public class DelegateBase implements DlgDelegate.DlgClickNotify, + DlgDelegate.HasDlgDelegate, + MultiService.MultiEventListener { private DlgDelegate m_delegate; @@ -52,17 +54,27 @@ public class DelegateBase implements DlgDelegate.DlgClickNotify { m_delegate.showNotAgainDlgThen( msgID, prefsKey, action, params ); } - protected void showNotAgainDlgThen( int msgID, int prefsKey, - Action action ) + public void showNotAgainDlgThen( int msgID, int prefsKey, Action action ) { m_delegate.showNotAgainDlgThen( msgID, prefsKey, action ); } + protected void showNotAgainDlgThen( String msg, int prefsKey, + Action action ) + { + m_delegate.showNotAgainDlgThen( msg, prefsKey, action, null ); + } + protected void showNotAgainDlg( int msgID, int prefsKey ) { m_delegate.showNotAgainDlgThen( msgID, prefsKey ); } + protected void showNotAgainDlgThen( int msgID, int prefsKey ) + { + m_delegate.showNotAgainDlgThen( msgID, prefsKey ); + } + // It sucks that these must be duplicated here and XWActivity protected void showAboutDialog() { @@ -96,6 +108,11 @@ public class DelegateBase implements DlgDelegate.DlgClickNotify { m_delegate.showConfirmThen( msg, posButton, action, params ); } + protected void showConfirmThen( int msgID, Action action ) + { + m_delegate.showConfirmThen( msgID, action ); + } + protected boolean post( Runnable runnable ) { return m_delegate.post( runnable ); @@ -116,6 +133,34 @@ public class DelegateBase implements DlgDelegate.DlgClickNotify { m_delegate.launchLookup( words, lang, false ); } + protected void showInviteChoicesThen( Action action ) + { + m_delegate.showInviteChoicesThen( action ); + } + + protected void startProgress( int id ) + { + m_delegate.startProgress( id ); + } + + protected void stopProgress() + { + m_delegate.stopProgress(); + } + + protected void showDictGoneFinish() + { + m_delegate.showDictGoneFinish(); + } + + ////////////////////////////////////////////////// + // MultiService.MultiEventListener interface + ////////////////////////////////////////////////// + public void eventOccurred( MultiService.MultiEvent event, final Object ... args ) + { + Assert.fail(); + } + ////////////////////////////////////////////////////////////////////// // DlgDelegate.DlgClickNotify interface ////////////////////////////////////////////////////////////////////// diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java index d409a1750..2d6bb5c53 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java @@ -111,6 +111,7 @@ public class DlgDelegate { public interface HasDlgDelegate { void showOKOnlyDialog( int msgID ); void showOKOnlyDialog( String msg ); + void showNotAgainDlgThen( int msgID, int prefsKey, Action action ); } private Activity m_activity; @@ -270,6 +271,12 @@ public class DlgDelegate { showConfirmThen( msg, R.string.button_ok, action, null ); } + public void showConfirmThen( int msgID, Action action ) + { + showConfirmThen( m_activity.getString( msgID ), + R.string.button_ok, action, null ); + } + public void showConfirmThen( String msg, Action action, Object[] params ) { showConfirmThen( msg, R.string.button_ok, action, params ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java index 88b0705e0..813991448 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java @@ -853,7 +853,7 @@ public class RelayService extends XWService { DbgUtils.logf( "RelayService::feedMessage: %d bytes for rowid %d", msg.length, rowid ); - if ( BoardActivity.feedMessage( rowid, msg ) ) { + if ( BoardDelegate.feedMessage( rowid, msg ) ) { DbgUtils.logf( "feedMessage: board ate it" ); // do nothing } else { @@ -893,7 +893,7 @@ public class RelayService extends XWService // if game has messages, open it and feed 'em to it. if ( null != forOne ) { sink.setRowID( rowIDs[ii] ); - if ( BoardActivity.feedMessages( rowIDs[ii], forOne ) + if ( BoardDelegate.feedMessages( rowIDs[ii], forOne ) || GameUtils.feedMessages( this, rowIDs[ii], forOne, null, sink ) ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java index 6bed48f93..42225e25f 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java @@ -610,7 +610,7 @@ public class SMSService extends XWService { sendDiedPacket( addr.sms_phone, gameID ); } else { for ( long rowid : rowids ) { - if ( BoardActivity.feedMessage( gameID, msg, addr ) ) { + if ( BoardDelegate.feedMessage( gameID, msg, addr ) ) { // do nothing } else { SMSMsgSink sink = new SMSMsgSink( this ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Toolbar.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Toolbar.java index 4318d150a..ab26e9044 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Toolbar.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Toolbar.java @@ -29,6 +29,7 @@ import android.widget.LinearLayout; import android.widget.ImageButton; import org.eehouse.android.xw4.DlgDelegate.Action; +import org.eehouse.android.xw4.DlgDelegate.HasDlgDelegate; import org.eehouse.android.xw4.jni.*; public class Toolbar { @@ -71,7 +72,8 @@ public class Toolbar { new TBButtonInfo( R.id.values_button_horizontal ), }; - private XWActivity m_activity; + private Activity m_activity; + private DlgDelegate.HasDlgDelegate m_dlgDlgt; private View m_me; private enum ORIENTATION { ORIENT_UNKNOWN, @@ -80,9 +82,10 @@ public class Toolbar { }; private ORIENTATION m_curOrient = ORIENTATION.ORIENT_UNKNOWN; - public Toolbar( XWActivity activity, int id ) + public Toolbar( Activity activity, HasDlgDelegate dlgDlgt, int id ) { m_activity = activity; + m_dlgDlgt = dlgDlgt; m_me = activity.findViewById( id ); } @@ -122,7 +125,7 @@ public class Toolbar { { View.OnClickListener listener = new View.OnClickListener() { public void onClick( View view ) { - m_activity.showNotAgainDlgThen( msgID, prefsKey, action ); + m_dlgDlgt.showNotAgainDlgThen( msgID, prefsKey, action ); } }; setListener( index, listener ); @@ -133,7 +136,7 @@ public class Toolbar { { View.OnLongClickListener listener = new View.OnLongClickListener() { public boolean onLongClick( View view ) { - m_activity.showNotAgainDlgThen( msgID, prefsKey, action ); + m_dlgDlgt.showNotAgainDlgThen( msgID, prefsKey, action ); return true; } }; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java index e4fbadf33..ce2a884d0 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java @@ -115,8 +115,7 @@ public class XWActivity extends Activity m_delegate.showNotAgainDlgThen( msg, prefsKey, action, null ); } - protected void showNotAgainDlgThen( int msgID, int prefsKey, - Action action ) + public void showNotAgainDlgThen( int msgID, int prefsKey, Action action ) { m_delegate.showNotAgainDlgThen( msgID, prefsKey, action ); } 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 0f4981cba..c77d8833c 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 @@ -20,12 +20,12 @@ package org.eehouse.android.xw4.jni; -import org.eehouse.android.xw4.XWActivity; +import android.app.Activity; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; public interface BoardHandler { - void startHandling( XWActivity parent, JNIThread thread, + void startHandling( Activity parent, JNIThread thread, int gamePtr, CurGameInfo gi, CommsConnType connType );