Get play via BT working!!! Replace BTConnection with BTService to make

keeping the listener/sender threads going much easier.  Create new
message sink to dispatch incoming messages, and start passing return
addresses into comms (not required before when relay was only
transport as it has no return addresses.)
This commit is contained in:
Eric House 2012-01-30 06:39:18 -08:00
parent 3f03232008
commit 094d08e903
15 changed files with 821 additions and 590 deletions

View file

@ -141,16 +141,16 @@
</intent-filter>
</receiver>
<receiver android:name=".BTConnection">
<intent-filter>
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
</intent-filter>
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
</intent-filter>
</receiver>
<!-- <receiver android:name=".BTConnection"> -->
<!-- <intent-filter> -->
<!-- <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" /> -->
<!-- </intent-filter> -->
<!-- <intent-filter> -->
<!-- <action android:name="android.bluetooth.device.action.ACL_CONNECTED" /> -->
<!-- <action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" /> -->
<!-- <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" /> -->
<!-- </intent-filter> -->
<!-- </receiver> -->
<!-- <receiver android:name="NBSReceiver"> -->
<!-- <intent-filter android:priority="10"> -->
@ -160,5 +160,7 @@
<!-- </intent-filter> -->
<!-- </receiver> -->
<service android:name="BTService"/>
</application>
</manifest>

View file

@ -1039,7 +1039,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_comms_1setAddr
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1receiveMessage
( JNIEnv* env, jclass C, jint gamePtr, jbyteArray jstream )
( JNIEnv* env, jclass C, jint gamePtr, jbyteArray jstream, jobject jaddr )
{
jboolean result;
XWJNI_START_GLOBALS();
@ -1048,7 +1048,13 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1receiveMessage
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, globals->vtMgr,
jstream );
result = comms_checkIncomingStream( state->game.comms, stream, NULL );
CommsAddrRec* addrp = NULL;
CommsAddrRec addr;
if ( NULL != jaddr ) {
getJAddrRec( env, &addr, jaddr );
addrp = &addr;
}
result = comms_checkIncomingStream( state->game.comms, stream, addrp );
if ( result ) {
ServerCtxt* server = state->game.server;
(void)server_do( server );

View file

@ -150,6 +150,44 @@ public class BoardActivity extends XWActivity
private int m_missing;
private boolean m_haveInvited = false;
private static BoardActivity s_this = null;
private static Object s_thisLocker = new Object();
public static boolean feedMessage( int gameID, byte[] msg,
CommsAddrRec retAddr )
{
boolean delivered = false;
synchronized( s_thisLocker ) {
if ( null != s_this ) {
Assert.assertNotNull( s_this.m_gi );
Assert.assertNotNull( s_this.m_gameLock );
Assert.assertNotNull( s_this.m_jniThread );
if ( gameID == s_this.m_gi.gameID ) {
s_this.m_jniThread.handle( JNICmd.CMD_RECEIVE, msg,
retAddr );
delivered = true;
}
}
}
return delivered;
}
private static void setThis( BoardActivity self )
{
synchronized( s_thisLocker ) {
Assert.assertNull( s_this );
s_this = self;
}
}
private static void clearThis()
{
synchronized( s_thisLocker ) {
Assert.assertNotNull( s_this );
s_this = null;
}
}
public class TimerRunnable implements Runnable {
private int m_why;
private int m_when;
@ -1319,6 +1357,8 @@ public class BoardActivity extends XWActivity
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 ) {
@ -1551,6 +1591,8 @@ public class BoardActivity extends XWActivity
m_jniThread = null;
}
clearThis();
XwJNI.game_dispose( m_jniGamePtr );
m_jniGamePtr = 0;
m_gi = null;

View file

@ -303,7 +303,7 @@ public class CommsTransport implements TransportProcs,
m_haveLen += wantLen;
if ( m_haveLen == m_packetIn.length ) {
// send completed packet
m_jniThread.handle( JNICmd.CMD_RECEIVE, m_packetIn );
m_jniThread.handle( JNICmd.CMD_RECEIVE, m_packetIn, null );
m_packetIn = null;
}
}
@ -388,7 +388,9 @@ public class CommsTransport implements TransportProcs,
// }
break;
case COMMS_CONN_BT:
nSent = BTConnection.enqueueFor( buf, m_addr.bt_hostName, gameID );
String hostName = m_addr.bt_hostName;
nSent = BTService.enqueueFor( m_context, buf, m_addr.bt_hostName,
gameID );
break;
default:
Assert.fail();

View file

@ -394,6 +394,25 @@ public class DBUtils {
return result;
}
public static long getRowIDFor( Context context, int gameID )
{
long result = ROWID_NOTFOUND;
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
String[] columns = { ROW_ID };
String selection = String.format( DBHelper.GAMEID + "=%d", gameID );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
result = cursor.getLong( cursor.getColumnIndex(ROW_ID) );
}
cursor.close();
db.close();
}
return result;
}
public static long getRowIDForOpen( Context context, NetLaunchInfo nli )
{
long result = ROWID_NOTFOUND;

View file

@ -622,53 +622,78 @@ public class GameUtils {
}
}
public static boolean feedMessages( Context context, String relayID,
byte[][] msgs, RelayMsgSink sink )
private static boolean feedMessages( Context context, long rowid,
byte[][] msgs, CommsAddrRec ret,
MultiMsgSink sink )
{
boolean draw = false;
long rowid = DBUtils.getRowIDFor( context, relayID );
if ( -1 != rowid ) {
CurGameInfo gi = new CurGameInfo( context );
FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid );
GameLock lock = new GameLock( rowid, true );
if ( lock.tryLock() ) {
int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock );
Assert.assertTrue( -1 != rowid );
CurGameInfo gi = new CurGameInfo( context );
FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid );
GameLock lock = new GameLock( rowid, true );
if ( lock.tryLock() ) {
int gamePtr = loadMakeGame( context, gi, feedImpl, sink, lock );
XwJNI.comms_resendAll( gamePtr );
XwJNI.comms_resendAll( gamePtr );
if ( null != msgs ) {
for ( byte[] msg : msgs ) {
draw = XwJNI.game_receiveMessage( gamePtr, msg )
|| draw;
}
if ( null != msgs ) {
for ( byte[] msg : msgs ) {
draw = XwJNI.game_receiveMessage( gamePtr, msg, ret )
|| draw;
}
// update gi to reflect changes due to messages
XwJNI.game_getGi( gamePtr, gi );
saveGame( context, gamePtr, gi, lock, false );
summarizeAndClose( context, lock, gamePtr, gi, feedImpl );
int flags = GameSummary.MSG_FLAGS_NONE;
if ( feedImpl.m_gotChat ) {
flags |= GameSummary.MSG_FLAGS_CHAT;
}
if ( feedImpl.m_gotMsg ) {
flags |= GameSummary.MSG_FLAGS_TURN;
}
if ( feedImpl.m_gameOver ) {
flags |= GameSummary.MSG_FLAGS_GAMEOVER;
}
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
draw = true;
DBUtils.setMsgFlags( rowid, flags );
}
lock.unlock();
}
// update gi to reflect changes due to messages
XwJNI.game_getGi( gamePtr, gi );
saveGame( context, gamePtr, gi, lock, false );
summarizeAndClose( context, lock, gamePtr, gi, feedImpl );
int flags = GameSummary.MSG_FLAGS_NONE;
if ( feedImpl.m_gotChat ) {
flags |= GameSummary.MSG_FLAGS_CHAT;
}
if ( feedImpl.m_gotMsg ) {
flags |= GameSummary.MSG_FLAGS_TURN;
}
if ( feedImpl.m_gameOver ) {
flags |= GameSummary.MSG_FLAGS_GAMEOVER;
}
if ( GameSummary.MSG_FLAGS_NONE != flags ) {
draw = true;
DBUtils.setMsgFlags( rowid, flags );
}
lock.unlock();
}
DbgUtils.logf( "feedMessages=>%b", draw );
return draw;
} // feedMessages
public static boolean feedMessage( Context context, int gameID, byte[] msg,
CommsAddrRec ret, MultiMsgSink sink )
{
long rowid = DBUtils.getRowIDFor( context, gameID );
boolean draw = -1 != rowid;
if ( draw ) {
byte[][] msgs = new byte[1][];
msgs[0] = msg;
draw = feedMessages( context, rowid, msgs, ret, sink );
}
return draw;
}
// Current assumption: this is the relay case where return address
// can be null.
public static boolean feedMessages( Context context, String relayID,
byte[][] msgs, MultiMsgSink sink )
{
long rowid = DBUtils.getRowIDFor( context, relayID );
boolean draw = -1 != rowid;
if ( draw ) {
draw = feedMessages( context, rowid, msgs, null, sink );
}
return draw;
}
// This *must* involve a reset if the language is changing!!!
// Which isn't possible right now, so make sure the old and new
// dict have the same langauge code.

View file

@ -54,7 +54,8 @@ import org.eehouse.android.xw4.jni.*;
public class GamesList extends XWListActivity
implements DispatchNotify.HandleRelaysIface,
DBUtils.DBChangeListener,
GameListAdapter.LoadItemCB {
GameListAdapter.LoadItemCB,
BTService.BTEventListener { // for ping results
private static final int WARN_NODICT = DlgDelegate.DIALOG_LAST + 1;
private static final int WARN_NODICT_SUBST = WARN_NODICT + 1;
@ -322,6 +323,18 @@ public class GamesList extends XWListActivity
// PhoneStateListener.LISTEN_DATA_CONNECTION_STATE );
}
@Override
protected void onResume() {
super.onResume();
BTService.setBTEventListener( this );
}
@Override
protected void onPause() {
BTService.setBTEventListener( null );
super.onPause();
}
@Override
protected void onStop()
{
@ -409,6 +422,21 @@ public class GamesList extends XWListActivity
onContentChanged();
}
// BTService.BTEventListener interface
public void eventOccurred( BTService.BTEvent event, final Object ... args )
{
switch( event ) {
case HOST_PONGED:
m_handler.post( new Runnable() {
public void run() {
DbgUtils.showf( GamesList.this,
"Pong from %s", args[0].toString() );
}
});
break;
}
}
// DlgDelegate.DlgClickNotify interface
@Override
public void dlgButtonClicked( int id, int which )
@ -517,18 +545,7 @@ public class GamesList extends XWListActivity
switch (item.getItemId()) {
case R.id.gamel_menu_btping:
Handler handler = new Handler() {
public void handleMessage( Message msg ) {
switch( msg.what ) {
case BTConnection.GOT_PONG:
String name = (String)msg.obj;
DbgUtils.showf( GamesList.this, "Pong from %s!!!",
name );
break;
}
}
};
BTConnection.ping( handler );
BTService.ping( this );
break;
case R.id.gamel_menu_newgame:

View file

@ -0,0 +1,61 @@
/* -*- compile-command: "cd ../../../../../; ant install"; -*- */
/*
* Copyright 2009 - 2012 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.content.Context;
import java.util.HashMap;
import java.util.ArrayList;
import junit.framework.Assert;
import org.eehouse.android.xw4.jni.*;
public class MultiMsgSink implements TransportProcs {
/***** TransportProcs interface *****/
public int getFlags() { return COMMS_XPORT_FLAGS_HASNOCONN; }
public int transportSend( byte[] buf, final CommsAddrRec addr, int gameID )
{
Assert.fail();
return -1;
}
public void relayStatus( CommsRelayState newState )
{
}
public void relayErrorProc( XWRELAY_ERROR relayErr )
{
}
public void relayConnd( String room, int devOrder, boolean allHere,
int nMissing )
{
}
public boolean relayNoConnProc( byte[] buf, String relayID )
{
Assert.fail();
return false;
}
}

View file

@ -46,21 +46,25 @@ import org.eehouse.android.xw4.jni.XwJNI;
public class NewGameActivity extends XWActivity
implements BTConnection.BTStateChangeListener {
implements BTService.BTEventListener {
private static final int NEW_GAME_ACTION = 1;
private static final String SAVE_DEVNAMES = "DEVNAMES";
private static final int PICK_BTDEV_DLG = DlgDelegate.DIALOG_LAST + 1;
private boolean m_showsOn;
private Handler m_handler = null;
private ProgressDialog m_progress;
private int m_chosen;
private String[] m_btDevNames;
@Override
protected void onCreate(Bundle savedInstanceState)
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
getBundledData( savedInstanceState );
m_handler = new Handler();
setContentView( R.layout.new_game );
@ -122,21 +126,20 @@ public class NewGameActivity extends XWActivity
m_progress =
ProgressDialog.show( NewGameActivity.this, msg,
null, true, true );
BTConnection.rescan( NewGameActivity.this,
getHandler() );
BTService.rescan( NewGameActivity.this );
}
};
final String[] btDevs = BTConnection.listPairedWithXwords();
OnClickListener okLstnr =
new OnClickListener() {
public void onClick( DialogInterface dlg,
int whichButton ) {
if ( 0 <= m_chosen ) {
if ( m_chosen < btDevs.length ) {
if ( m_chosen < m_btDevNames.length ) {
int gameID = GameUtils.newGameID();
BTConnection.
inviteRemote( btDevs[m_chosen],
gameID, getHandler() );
BTService.
inviteRemote( NewGameActivity.this,
m_btDevNames[m_chosen],
gameID );
}
}
}
@ -147,7 +150,7 @@ public class NewGameActivity extends XWActivity
.setNegativeButton( R.string.bt_pick_rescan_button,
scanLstnr );
if ( null != btDevs && 0 < btDevs.length ) {
if ( null != m_btDevNames && 0 < m_btDevNames.length ) {
OnClickListener devChosenLstnr =
new OnClickListener() {
public void onClick( DialogInterface dlgi,
@ -161,7 +164,7 @@ public class NewGameActivity extends XWActivity
}
};
m_chosen = -1;
ab.setSingleChoiceItems( btDevs, m_chosen,
ab.setSingleChoiceItems( m_btDevNames, m_chosen,
devChosenLstnr );
}
dialog = ab.create();
@ -201,19 +204,75 @@ public class NewGameActivity extends XWActivity
protected void onResume() {
super.onResume();
checkEnableBT( false );
BTConnection.setBTStateChangeListener( this );
BTService.setBTEventListener( this );
}
@Override
protected void onPause() {
BTConnection.setBTStateChangeListener( null );
BTService.setBTEventListener( null );
super.onPause();
}
// BTConnection.BTStateChangeListener
public void stateChanged( boolean nowEnabled )
@Override
protected void onSaveInstanceState( Bundle outState )
{
checkEnableBT( false );
super.onSaveInstanceState( outState );
outState.putStringArray( SAVE_DEVNAMES, m_btDevNames );
}
private void getBundledData( Bundle bundle )
{
if ( null != bundle ) {
m_btDevNames = bundle.getStringArray( SAVE_DEVNAMES );
}
}
// BTService.BTEventListener interface
public void eventOccurred( BTService.BTEvent event, final Object ... args )
{
switch( event ) {
case SCAN_DONE:
m_handler.post( new Runnable() {
public void run() {
synchronized( NewGameActivity.this ) {
if ( null != m_progress ) {
m_progress.cancel();
m_progress = null;
}
if ( 0 < args.length ) {
m_btDevNames = (String[])(args[0]);
}
showDialog( PICK_BTDEV_DLG );
}
}
} );
break;
case BT_ENABLED:
case BT_DISABLED:
m_handler.post( new Runnable() {
public void run() {
checkEnableBT( false );
}
});
break;
case NEWGAME_FAILURE:
m_handler.post( new Runnable() {
public void run() {
DbgUtils.showf( NewGameActivity.this,
"Remote failed to create game" );
}
});
break;
case NEWGAME_SUCCESS:
int gameID = (Integer)args[0];
GameUtils.makeNewBTGame( NewGameActivity.this, gameID, null );
finish();
break;
default:
DbgUtils.logf( "unexpected event %s", event.toString() );
Assert.fail();
break;
}
}
private void makeNewGame( boolean networked, boolean launch )
@ -269,7 +328,7 @@ public class NewGameActivity extends XWActivity
private void checkEnableBT( boolean force )
{
boolean enabled = BTConnection.BTEnabled();
boolean enabled = BTService.BTEnabled();
if ( force || enabled != m_showsOn ) {
m_showsOn = enabled;
@ -308,32 +367,4 @@ public class NewGameActivity extends XWActivity
}
}
}
private Handler getHandler()
{
if ( null == m_handler ) {
m_handler = new Handler() {
public void handleMessage( Message msg ) {
switch( msg.what ) {
case BTConnection.CONNECT_ACCEPTED:
GameUtils.makeNewBTGame( NewGameActivity.this,
msg.arg1, null );
finish();
break;
case BTConnection.CONNECT_REFUSED:
case BTConnection.CONNECT_FAILED:
break;
case BTConnection.SCAN_DONE:
if ( null != m_progress ) {
m_progress.cancel();
m_progress = null;
}
showDialog( PICK_BTDEV_DLG );
break;
}
}
};
}
return m_handler;
}
}

View file

@ -28,7 +28,7 @@ import junit.framework.Assert;
import org.eehouse.android.xw4.jni.*;
public class RelayMsgSink implements TransportProcs {
public class RelayMsgSink extends MultiMsgSink {
private HashMap<String,ArrayList<byte[]>> m_msgLists = null;
@ -39,27 +39,6 @@ public class RelayMsgSink implements TransportProcs {
/***** TransportProcs interface *****/
public int getFlags() { return COMMS_XPORT_FLAGS_HASNOCONN; }
public int transportSend( byte[] buf, final CommsAddrRec addr, int gameID )
{
Assert.fail();
return -1;
}
public void relayStatus( CommsRelayState newState )
{
}
public void relayErrorProc( XWRELAY_ERROR relayErr )
{
}
public void relayConnd( String room, int devOrder, boolean allHere,
int nMissing )
{
}
public boolean relayNoConnProc( byte[] buf, String relayID )
{
if ( null == m_msgLists ) {

View file

@ -42,6 +42,7 @@ public class XWApp extends Application {
DbgUtils.logEnable( this );
RelayReceiver.RestartTimer( this );
BTService.startService( this );
}
public static UUID getAppUUID() {

View file

@ -72,6 +72,12 @@ public class CommsAddrRec {
this( context, CommsConnType.COMMS_CONN_RELAY );
}
public CommsAddrRec( Context context, String btHost )
{
this( context, CommsConnType.COMMS_CONN_BT );
bt_hostName = btHost;
}
public CommsAddrRec( final CommsAddrRec src )
{
this.copyFrom( src );
@ -96,5 +102,7 @@ public class CommsAddrRec {
ip_relay_port = src.ip_relay_port;
ip_relay_seeksPublicRoom = src.ip_relay_seeksPublicRoom;
ip_relay_advertiseRoom = src.ip_relay_advertiseRoom;
bt_hostName = src.bt_hostName;
}
}

View file

@ -349,7 +349,8 @@ public class JNIThread extends Thread {
case CMD_RECEIVE:
draw = XwJNI.game_receiveMessage( m_jniGamePtr,
(byte[])args[0] );
(byte[])args[0],
(CommsAddrRec)args[1]);
handle( JNICmd.CMD_DO );
if ( draw ) {
handle( JNICmd.CMD_SAVE );

View file

@ -122,7 +122,8 @@ public class XwJNI {
}
public static native boolean game_receiveMessage( int gamePtr,
byte[] stream );
byte[] stream,
CommsAddrRec retAddr );
public static native void game_summarize( int gamePtr, GameSummary summary );
public static native byte[] game_saveToStream( int gamePtr,
CurGameInfo gi );