factor upward code from BTEvent so that SMSService can use same

mechanism to send asynchronous results, then use to notify when a game
on other end has been deleted.  This flushed out a bug where a rowid
will get reused and a new game wind up with data from the previous
one -- but otherwise works.
This commit is contained in:
Eric House 2012-04-06 23:00:47 -07:00
parent d9ba0c4229
commit d6a3aafaec
12 changed files with 177 additions and 78 deletions

View file

@ -54,3 +54,4 @@ XWListActivity.java
XWListAdapter.java XWListAdapter.java
XWListItem.java XWListItem.java
XWListPreference.java XWListPreference.java
MultiService.java

View file

@ -59,7 +59,7 @@ public class BTInviteActivity extends InviteActivity
// BTService.BTEventListener interface // BTService.BTEventListener interface
@Override @Override
public void eventOccurred( BTService.BTEvent event, final Object ... args ) public void eventOccurred( MultiService.MultiEvent event, final Object ... args )
{ {
switch( event ) { switch( event ) {
case SCAN_DONE: case SCAN_DONE:

View file

@ -46,30 +46,12 @@ import java.util.concurrent.TimeUnit;
import junit.framework.Assert; import junit.framework.Assert;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommonPrefs; import org.eehouse.android.xw4.jni.CommonPrefs;
import org.eehouse.android.xw4.jni.CommsAddrRec; import org.eehouse.android.xw4.jni.CommsAddrRec;
public class BTService extends Service { public class BTService extends Service {
public enum BTEvent { BAD_PROTO
, BT_ENABLED
, BT_DISABLED
, SCAN_DONE
, HOST_PONGED
, NEWGAME_SUCCESS
, NEWGAME_FAILURE
, MESSAGE_ACCEPTED
, MESSAGE_REFUSED
, MESSAGE_NOGAME
, MESSAGE_RESEND
, MESSAGE_FAILOUT
, MESSAGE_DROPPED
};
public interface BTEventListener {
public void eventOccurred( BTEvent event, Object ... args );
}
private static final long RESEND_TIMEOUT = 5; // seconds private static final long RESEND_TIMEOUT = 5; // seconds
private static final int MAX_SEND_FAIL = 3; private static final int MAX_SEND_FAIL = 3;
@ -96,8 +78,7 @@ public class BTService extends Service {
private static final String NTO_STR = "TOT"; private static final String NTO_STR = "TOT";
private static final String NHE_STR = "HER"; private static final String NHE_STR = "HER";
private static BTEventListener s_eventListener = null; private static MultiService s_srcMgr = new MultiService();
private static Object s_syncObj = new Object();
private enum BTCmd { private enum BTCmd {
PING, PING,
@ -167,10 +148,9 @@ public class BTService extends Service {
} }
} }
public static void setBTEventListener( BTEventListener li ) { public static MultiService getMultiEventSrc()
synchronized( s_syncObj ) { {
s_eventListener = li; return s_srcMgr;
}
} }
public static void radioChanged( Context context, boolean cameOn ) public static void radioChanged( Context context, boolean cameOn )
@ -306,7 +286,8 @@ public class BTService extends Service {
break; break;
case RADIO: case RADIO:
boolean cameOn = intent.getBooleanExtra( RADIO_STR, false ); boolean cameOn = intent.getBooleanExtra( RADIO_STR, false );
BTEvent evt = cameOn? BTEvent.BT_ENABLED : BTEvent.BT_DISABLED; MultiEvent evt = cameOn? MultiEvent.BT_ENABLED
: MultiEvent.BT_DISABLED;
sendResult( evt ); sendResult( evt );
if ( !cameOn ) { if ( !cameOn ) {
stopListener(); stopListener();
@ -368,7 +349,7 @@ public class BTService extends Service {
byte proto = inStream.readByte(); byte proto = inStream.readByte();
if ( proto != BT_PROTO ) { if ( proto != BT_PROTO ) {
socket.close(); socket.close();
sendResult( BTEvent.BAD_PROTO, sendResult( MultiEvent.BAD_PROTO,
socket.getRemoteDevice().getName() ); socket.getRemoteDevice().getName() );
} else { } else {
byte msg = inStream.readByte(); byte msg = inStream.readByte();
@ -633,7 +614,7 @@ public class BTService extends Service {
switch( elem.m_cmd ) { switch( elem.m_cmd ) {
case PING: case PING:
sendPings( BTEvent.HOST_PONGED ); sendPings( MultiEvent.HOST_PONGED );
break; break;
case SCAN: case SCAN:
sendPings( null ); sendPings( null );
@ -656,7 +637,7 @@ public class BTService extends Service {
} }
} // run } // run
private void sendPings( BTEvent event ) private void sendPings( MultiEvent event )
{ {
Set<BluetoothDevice> pairedDevs = m_adapter.getBondedDevices(); Set<BluetoothDevice> pairedDevs = m_adapter.getBondedDevices();
DbgUtils.logf( "ping: got %d paired devices", pairedDevs.size() ); DbgUtils.logf( "ping: got %d paired devices", pairedDevs.size() );
@ -725,8 +706,8 @@ public class BTService extends Service {
} }
socket.close(); socket.close();
BTEvent evt = success ? BTEvent.NEWGAME_SUCCESS MultiEvent evt = success ? MultiEvent.NEWGAME_SUCCESS
: BTEvent.NEWGAME_FAILURE; : MultiEvent.NEWGAME_FAILURE;
sendResult( evt, elem.m_gameID ); sendResult( evt, elem.m_gameID );
} }
} catch ( java.io.IOException ioe ) { } catch ( java.io.IOException ioe ) {
@ -740,13 +721,13 @@ public class BTService extends Service {
synchronized( m_deadGames ) { synchronized( m_deadGames ) {
success = m_deadGames.contains( elem.m_gameID ); success = m_deadGames.contains( elem.m_gameID );
} }
BTEvent evt; MultiEvent evt;
if ( success ) { if ( success ) {
evt = BTEvent.MESSAGE_DROPPED; evt = MultiEvent.MESSAGE_DROPPED;
DbgUtils.logf( "dropping message because game %X dead", DbgUtils.logf( "dropping message because game %X dead",
elem.m_gameID ); elem.m_gameID );
} else { } else {
evt = BTEvent.MESSAGE_REFUSED; evt = MultiEvent.MESSAGE_REFUSED;
} }
if ( !success ) { if ( !success ) {
try { try {
@ -775,10 +756,10 @@ public class BTService extends Service {
switch ( reply ) { switch ( reply ) {
case MESG_ACCPT: case MESG_ACCPT:
evt = BTEvent.MESSAGE_ACCEPTED; evt = MultiEvent.MESSAGE_ACCEPTED;
break; break;
case MESG_GAMEGONE: case MESG_GAMEGONE:
evt = BTEvent.MESSAGE_NOGAME; evt = MultiEvent.MESSAGE_NOGAME;
break; break;
} }
} }
@ -793,7 +774,7 @@ public class BTService extends Service {
sendResult( evt, elem.m_gameID, 0, elem.m_recipient ); sendResult( evt, elem.m_gameID, 0, elem.m_recipient );
if ( ! success ) { if ( ! success ) {
int failCount = elem.incrFailCount(); int failCount = elem.incrFailCount();
sendResult( BTEvent.MESSAGE_RESEND, elem.m_recipient, sendResult( MultiEvent.MESSAGE_RESEND, elem.m_recipient,
RESEND_TIMEOUT, failCount ); RESEND_TIMEOUT, failCount );
} }
return success; return success;
@ -811,7 +792,7 @@ public class BTService extends Service {
if ( success ) { if ( success ) {
iter.remove(); iter.remove();
} else if ( elem.failCountExceeded() ) { } else if ( elem.failCountExceeded() ) {
sendResult( BTEvent.MESSAGE_FAILOUT, elem.m_recipient ); sendResult( MultiEvent.MESSAGE_FAILOUT, elem.m_recipient );
iter.remove(); iter.remove();
} }
} }
@ -862,17 +843,12 @@ public class BTService extends Service {
private void sendNames() private void sendNames()
{ {
sendResult( BTEvent.SCAN_DONE, (Object)(names()) ); sendResult( MultiEvent.SCAN_DONE, (Object)(names()) );
} }
private void sendResult( BTEvent event, Object ... args ) private void sendResult( MultiEvent event, Object ... args )
{ {
Assert.assertNotNull( event ); s_srcMgr.sendResult( event, args );
synchronized( s_syncObj ) {
if ( null != s_eventListener ) {
s_eventListener.eventOccurred( event, args );
}
}
} }
private void listLocalBTGames( boolean force ) private void listLocalBTGames( boolean force )

View file

@ -821,22 +821,26 @@ public class BoardActivity extends XWActivity
////////////////////////////////////////////////// //////////////////////////////////////////////////
@Override @Override
@SuppressWarnings("fallthrough") @SuppressWarnings("fallthrough")
public void eventOccurred( BTService.BTEvent event, final Object ... args ) public void eventOccurred( MultiService.MultiEvent event, final Object ... args )
{ {
switch( event ) { switch( event ) {
case MESSAGE_ACCEPTED: case MESSAGE_ACCEPTED:
case MESSAGE_REFUSED: case MESSAGE_REFUSED:
if ( null != m_jniThread ) { if ( null != m_jniThread ) {
boolean accepted = BTService.BTEvent.MESSAGE_ACCEPTED == event; boolean accepted =
MultiService.MultiEvent.MESSAGE_ACCEPTED == event;
m_jniThread.handle( JNICmd.CMD_DRAW_BT_STATUS, accepted ); m_jniThread.handle( JNICmd.CMD_DRAW_BT_STATUS, accepted );
} }
break; break;
case MESSAGE_NOGAME: case MESSAGE_NOGAME:
post( new Runnable() { int gameID = (Integer)args[0];
public void run() { if ( gameID == m_gi.gameID ) {
showDialog( DLG_DELETED ); post( new Runnable() {
} public void run() {
} ); showDialog( DLG_DELETED );
}
} );
}
break; break;
case NEWGAME_FAILURE: case NEWGAME_FAILURE:
DbgUtils.logf( "failed to create game" ); DbgUtils.logf( "failed to create game" );

View file

@ -267,7 +267,7 @@ public class DlgDelegate {
return true; return true;
} }
public void eventOccurred( BTService.BTEvent event, final Object ... args ) public void eventOccurred( MultiService.MultiEvent event, final Object ... args )
{ {
String msg = null; String msg = null;
boolean asToast = true; boolean asToast = true;

View file

@ -283,6 +283,7 @@ public class GameUtils {
public static void deleteGame( Context context, long rowid, public static void deleteGame( Context context, long rowid,
boolean informNow ) boolean informNow )
{ {
DbgUtils.logf( "deleteGame(rowid=%d)", rowid );
// does this need to be synchronized? // does this need to be synchronized?
GameLock lock = new GameLock( rowid, true ); GameLock lock = new GameLock( rowid, true );
if ( lock.tryLock() ) { if ( lock.tryLock() ) {
@ -839,11 +840,16 @@ public class GameUtils {
boolean informNow ) boolean informNow )
{ {
GameSummary summary = DBUtils.getSummary( context, lock ); GameSummary summary = DBUtils.getSummary( context, lock );
if ( null != summary.relayID ) { switch( summary.conType ) {
case COMMS_CONN_RELAY:
tellRelayDied( context, summary, informNow ); tellRelayDied( context, summary, informNow );
} break;
if ( 0 != summary.gameID ) { case COMMS_CONN_BT:
BTService.gameDied( context, summary.gameID ); BTService.gameDied( context, summary.gameID );
break;
case COMMS_CONN_SMS:
SMSService.gameDied( context, summary.gameID, summary.smsPhone );
break;
} }
} }

View file

@ -405,7 +405,8 @@ public class GamesList extends XWListActivity
// BTService.BTEventListener interface // BTService.BTEventListener interface
@Override @Override
public void eventOccurred( BTService.BTEvent event, final Object ... args ) public void eventOccurred( MultiService.MultiEvent event,
final Object ... args )
{ {
switch( event ) { switch( event ) {
case HOST_PONGED: case HOST_PONGED:

View file

@ -0,0 +1,65 @@
/* -*- compile-command: "cd ../../../../../; ant debug 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;
public class MultiService {
private BTEventListener m_li;
public enum MultiEvent { BAD_PROTO
, BT_ENABLED
, BT_DISABLED
, SCAN_DONE
, HOST_PONGED
, NEWGAME_SUCCESS
, NEWGAME_FAILURE
, MESSAGE_ACCEPTED
, MESSAGE_REFUSED
, MESSAGE_NOGAME
, MESSAGE_RESEND
, MESSAGE_FAILOUT
, MESSAGE_DROPPED
};
public interface BTEventListener {
public void eventOccurred( MultiEvent event, Object ... args );
}
// public interface MultiEventSrc {
// public void setBTEventListener( BTEventListener li );
// }
public void setListener( BTEventListener li )
{
synchronized( this ) {
m_li = li;
}
}
public void sendResult( MultiEvent event, Object ... args )
{
synchronized( this ) {
if ( null != m_li ) {
m_li.eventOccurred( event, args );
}
}
}
}

View file

@ -249,7 +249,8 @@ public class NewGameActivity extends XWActivity {
// BTService.BTEventListener interface // BTService.BTEventListener interface
@Override @Override
public void eventOccurred( BTService.BTEvent event, final Object ... args ) public void eventOccurred( MultiService.MultiEvent event,
final Object ... args )
{ {
switch( event ) { switch( event ) {
case BT_ENABLED: case BT_ENABLED:

View file

@ -40,6 +40,7 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import junit.framework.Assert; import junit.framework.Assert;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec; import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.XwJNI; import org.eehouse.android.xw4.jni.XwJNI;
@ -53,6 +54,8 @@ public class SMSService extends Service {
private static final int HANDLE = 1; private static final int HANDLE = 1;
private static final int INVITE = 2; private static final int INVITE = 2;
private static final int SEND = 3; private static final int SEND = 3;
private static final int REMOVE = 4;
private static final int MESG_GAMEGONE = 5;
private static final String CMD_STR = "CMD"; private static final String CMD_STR = "CMD";
private static final String BUFFER = "BUFFER"; private static final String BUFFER = "BUFFER";
@ -65,10 +68,11 @@ public class SMSService extends Service {
private static final String NPLAYERSH = "NPLAYERSH"; private static final String NPLAYERSH = "NPLAYERSH";
private static Boolean s_showToasts = null; private static Boolean s_showToasts = null;
private static MultiService s_srcMgr = new MultiService();
// All messages are base64-encoded byte arrays. The first byte is // All messages are base64-encoded byte arrays. The first byte is
// always one of these. What follows depends. // always one of these. What follows depends.
private enum SMS_CMD { NONE, INVITE, DATA, }; private enum SMS_CMD { NONE, INVITE, DATA, DEATH, };
private int m_nReceived = 0; private int m_nReceived = 0;
private static int s_nSent = 0; private static int s_nSent = 0;
@ -116,6 +120,14 @@ public class SMSService extends Service {
return binmsg.length; return binmsg.length;
} }
public static void gameDied( Context context, int gameID, String phone )
{
Intent intent = getIntentTo( context, REMOVE );
intent.putExtra( PHONE, phone );
intent.putExtra( GAMEID, gameID );
context.startService( intent );
}
public static String toPublicFmt( String msg ) public static String toPublicFmt( String msg )
{ {
String result; String result;
@ -139,6 +151,11 @@ public class SMSService extends Service {
return result; return result;
} }
public static MultiService getMultiEventSrc()
{
return s_srcMgr;
}
private static Intent getIntentTo( Context context, int cmd ) private static Intent getIntentTo( Context context, int cmd )
{ {
if ( null == s_showToasts ) { if ( null == s_showToasts ) {
@ -192,6 +209,11 @@ public class SMSService extends Service {
gameID = intent.getIntExtra( GAMEID, -1 ); gameID = intent.getIntExtra( GAMEID, -1 );
sendPacket( phone, gameID, bytes ); sendPacket( phone, gameID, bytes );
break; break;
case REMOVE:
gameID = intent.getIntExtra( GAMEID, -1 );
phone = intent.getStringExtra( PHONE );
sendDiedPacket( phone, gameID );
break;
} }
result = Service.START_STICKY; result = Service.START_STICKY;
@ -226,6 +248,19 @@ public class SMSService extends Service {
} }
} }
private void sendDiedPacket( String phone, int gameID )
{
ByteArrayOutputStream bas = new ByteArrayOutputStream( 32 );
DataOutputStream das = new DataOutputStream( bas );
try {
das.writeInt( gameID );
das.flush();
send( SMS_CMD.DEATH, bas.toByteArray(), phone );
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
}
public int sendPacket( String phone, int gameID, byte[] bytes ) public int sendPacket( String phone, int gameID, byte[] bytes )
{ {
DbgUtils.logf( "non-static SMSService.sendPacket()" ); DbgUtils.logf( "non-static SMSService.sendPacket()" );
@ -321,6 +356,10 @@ public class SMSService extends Service {
dis.read( rest ); dis.read( rest );
feedMessage( gameID, rest, addr ); feedMessage( gameID, rest, addr );
break; break;
case DEATH:
gameID = dis.readInt();
s_srcMgr.sendResult( MultiEvent.MESSAGE_NOGAME, gameID );
break;
default: default:
Assert.fail(); Assert.fail();
break; break;
@ -438,17 +477,17 @@ public class SMSService extends Service {
private void feedMessage( int gameID, byte[] msg, CommsAddrRec addr ) private void feedMessage( int gameID, byte[] msg, CommsAddrRec addr )
{ {
if ( BoardActivity.feedMessage( gameID, msg, addr ) ) { long rowid = DBUtils.getRowIDFor( this, gameID );
if ( DBUtils.ROWID_NOTFOUND == rowid ) {
sendDiedPacket( addr.sms_phone, gameID );
} else if ( BoardActivity.feedMessage( gameID, msg, addr ) ) {
// do nothing // do nothing
} else { } else {
long rowid = DBUtils.getRowIDFor( this, gameID ); SMSMsgSink sink = new SMSMsgSink( this );
if ( DBUtils.ROWID_NOTFOUND != rowid ) { if ( GameUtils.feedMessage( this, rowid, msg, addr, sink ) ) {
SMSMsgSink sink = new SMSMsgSink( this ); postNotification( gameID, R.string.new_smsmove_title,
if ( GameUtils.feedMessage( this, rowid, msg, addr, sink ) ) { getString(R.string.new_move_body)
postNotification( gameID, R.string.new_smsmove_title, );
getString(R.string.new_move_body)
);
}
} }
} }
} }

View file

@ -33,7 +33,7 @@ import junit.framework.Assert;
import org.eehouse.android.xw4.jni.CommonPrefs; import org.eehouse.android.xw4.jni.CommonPrefs;
public class XWActivity extends Activity public class XWActivity extends Activity
implements DlgDelegate.DlgClickNotify, BTService.BTEventListener { implements DlgDelegate.DlgClickNotify, MultiService.BTEventListener {
private DlgDelegate m_delegate; private DlgDelegate m_delegate;
@ -57,7 +57,8 @@ public class XWActivity extends Activity
protected void onResume() protected void onResume()
{ {
DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this ); DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this );
BTService.setBTEventListener( this ); BTService.getMultiEventSrc().setListener( this );
SMSService.getMultiEventSrc().setListener( this );
super.onResume(); super.onResume();
} }
@ -65,7 +66,8 @@ public class XWActivity extends Activity
protected void onPause() protected void onPause()
{ {
DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this ); DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this );
BTService.setBTEventListener( null ); BTService.getMultiEventSrc().setListener( null );
SMSService.getMultiEventSrc().setListener( null );
super.onPause(); super.onPause();
} }
@ -180,7 +182,8 @@ public class XWActivity extends Activity
} }
// BTService.BTEventListener interface // BTService.BTEventListener interface
public void eventOccurred( BTService.BTEvent event, final Object ... args ) public void eventOccurred( MultiService.MultiEvent event,
final Object ... args )
{ {
m_delegate.eventOccurred( event, args ); m_delegate.eventOccurred( event, args );
} }

View file

@ -30,7 +30,7 @@ import junit.framework.Assert;
import org.eehouse.android.xw4.jni.CommonPrefs; import org.eehouse.android.xw4.jni.CommonPrefs;
public class XWListActivity extends ListActivity public class XWListActivity extends ListActivity
implements DlgDelegate.DlgClickNotify, BTService.BTEventListener { implements DlgDelegate.DlgClickNotify, MultiService.BTEventListener {
private DlgDelegate m_delegate; private DlgDelegate m_delegate;
@ -54,7 +54,8 @@ public class XWListActivity extends ListActivity
protected void onResume() protected void onResume()
{ {
DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this ); DbgUtils.logf( "%s.onResume(this=%H)", getClass().getName(), this );
BTService.setBTEventListener( this ); BTService.getMultiEventSrc().setListener( this );
SMSService.getMultiEventSrc().setListener( this );
super.onResume(); super.onResume();
} }
@ -62,7 +63,8 @@ public class XWListActivity extends ListActivity
protected void onPause() protected void onPause()
{ {
DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this ); DbgUtils.logf( "%s.onPause(this=%H)", getClass().getName(), this );
BTService.setBTEventListener( null ); BTService.getMultiEventSrc().setListener( null );
SMSService.getMultiEventSrc().setListener( null );
super.onPause(); super.onPause();
} }
@ -191,7 +193,8 @@ public class XWListActivity extends ListActivity
} }
// BTService.BTEventListener interface // BTService.BTEventListener interface
public void eventOccurred( BTService.BTEvent event, final Object ... args ) public void eventOccurred( MultiService.MultiEvent event,
final Object ... args )
{ {
m_delegate.eventOccurred( event, args ); m_delegate.eventOccurred( event, args );
} }