Replace most of NBSReceiver with new NBSService. Route outgoing (from

comms) and incoming (from NBSReceiver) messages through it.  Since
some messages are too big, add header/protocol for breaking them up
and reassemble.  With this commit complete robot-vs-robot games are
possible between two very new Samsung phones on T-mobile.  Older
phones and other networks not tested.
This commit is contained in:
Eric House 2012-03-19 07:56:55 -07:00
parent 8e3d8c20c1
commit 599d01bfc7
11 changed files with 520 additions and 217 deletions

View file

@ -36,7 +36,7 @@
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" />
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="8" />
<application android:icon="@drawable/icon48x48"
android:label="@string/app_name"
@ -170,5 +170,7 @@
</intent-filter>
</receiver>
<service android:name="NBSService"/>
</application>
</manifest>

View file

@ -52,3 +52,4 @@ XWListPreference.java
GameNamer.java
LookupActivity.java
NBSInviteActivity.java
NBSService.java

View file

@ -35,7 +35,7 @@
<!-- <uses-permission android:name="android.permission.RECEIVE_SMS" /> -->
<!-- <uses-permission android:name="android.permission.SEND_SMS" /> -->
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="7" />
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="8" />
<application android:icon="@drawable/icon48x48"
android:label="@string/app_name"

View file

@ -10,4 +10,4 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-7
target=android-8

View file

@ -1855,7 +1855,7 @@
Bluetooth play.</string>
<string name="new_btmove_title">New move via Bluetooth</string>
<string name="new_btmove_body">One or more moves has arrived</string>
<string name="new_move_body">One or more moves has arrived</string>
<string name="bt_devs_missing">Missing connections? Invite BT
devs? (I won\'t ask again until you reopen this game.)</string>

View file

@ -511,7 +511,7 @@ public class BTService extends Service {
buffer, addr,
m_btMsgSink ) ) {
postNotification( gameID, R.string.new_btmove_title,
R.string.new_btmove_body );
R.string.new_move_body );
// do nothing
} else {
DbgUtils.logf( "nobody took msg for gameID %X",

View file

@ -1736,7 +1736,7 @@ public class BoardActivity extends XWActivity
m_gi.nPlayers, 1 );
break;
case COMMS_CONN_SMS:
NBSReceiver.inviteRemote( this, dev, m_gi.gameID,
NBSService.inviteRemote( this, dev, m_gi.gameID,
gameName, m_gi.dictLang,
m_gi.nPlayers, 1 );
break;

View file

@ -235,6 +235,7 @@ public class CommsTransport implements TransportProcs,
// // Let other know I'm here
// m_jniThread.handle( JNIThread.JNICmd.CMD_RESEND );
// break;
// case COMMS_CONN_SMS:
// default:
// DbgUtils.logf( "tickle: unexpected type %s",
// addr.conType.toString() );
@ -379,33 +380,10 @@ public class CommsTransport implements TransportProcs,
}
break;
case COMMS_CONN_SMS:
Assert.fail();
// DbgUtils.logf( "sending via sms to %s:%d",
// m_addr.sms_phone, m_addr.sms_port );
// try {
// Intent intent = new Intent( m_context, StatusReceiver.class);
// PendingIntent pi
// = PendingIntent.getBroadcast( m_context, 0,
// intent, 0 );
// if ( 0 == m_addr.sms_port ) {
// SmsManager.getDefault().sendTextMessage( m_addr.sms_phone,
// null, "Hello world",
// pi, pi );
// DbgUtils.logf( "called sendTextMessage" );
// } else {
// SmsManager.getDefault().
// sendDataMessage( m_addr.sms_phone, (String)null,
// (short)m_addr.sms_port,
// buf, pi, pi );
// DbgUtils.logf( "called sendDataMessage" );
// }
// nSent = buf.length;
// } catch ( java.lang.IllegalArgumentException iae ) {
// DbgUtils.logf( iae.toString() );
// }
nSent = NBSService.sendPacket( m_context, addr.sms_phone,
gameID, buf );
break;
case COMMS_CONN_BT:
String hostName = addr.bt_hostName;
nSent = BTService.enqueueFor( m_context, buf, addr.bt_hostName,
addr.bt_btAddr, gameID );
break;

View file

@ -20,39 +20,20 @@
package org.eehouse.android.xw4;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Base64;
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.io.InputStream;
import java.util.Arrays;
import junit.framework.Assert;
import org.eehouse.android.xw4.jni.CommsAddrRec;
public class NBSReceiver extends BroadcastReceiver {
// All messages are base64-encoded byte arrays. The first byte is
// always one of these. What follows depends.
private enum NBS_CMS { NBS_CMD_NONE,
NBS_CMD_INVITE,
NBS_CMD_DATA,
};
@Override
public void onReceive( Context context, Intent intent )
{
DbgUtils.logf( "NBSReceiver::onReceive(intent=%s)!!!!",
intent.toString() );
DbgUtils.logf( "NBSReceiver::onReceive()" );
Bundle bundle = intent.getExtras();
if ( null != bundle ) {
@ -61,167 +42,12 @@ public class NBSReceiver extends BroadcastReceiver {
for ( int ii = 0; ii < nbses.length; ++ii ) {
nbses[ii] = SmsMessage.createFromPdu((byte[])pdus[ii]);
receiveBuffer( context, nbses[ii].getUserData(),
nbses[ii].getOriginatingAddress() );
String phone = nbses[ii].getOriginatingAddress();
DbgUtils.logf( "NBSReceiver: post format: %s", phone );
NBSService.handleFrom( context, nbses[ii].getUserData(),
phone );
}
}
}
static void tryNBSMessage( Context context, String phoneNo )
{
byte[] data = { 'a', 'b', 'c' };
SmsManager mgr = SmsManager.getDefault();
try {
/* online comment says providing PendingIntents prevents
random crashes */
PendingIntent sent = PendingIntent.getBroadcast( context,
0, new Intent(), 0 );
PendingIntent dlvrd = PendingIntent.getBroadcast( context, 0,
new Intent(), 0 );
mgr.sendDataMessage( phoneNo, null, (short)50009,
data, sent, dlvrd );
// PendingIntent sentIntent,
// PendingIntent deliveryIntent );
DbgUtils.logf( "sendDataMessage finished" );
} catch ( IllegalArgumentException iae ) {
DbgUtils.logf( "%s", iae.toString() );
DbgUtils.logf( "onReceive done" );
}
}
public static void inviteRemote( Context context, String phone,
int gameID, String gameName,
int lang, int nPlayersT,
int nPlayersH )
{
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
DataOutputStream das = new DataOutputStream( bas );
try {
das.writeInt( gameID );
das.writeUTF( gameName );
das.writeInt( lang );
das.writeByte( nPlayersT );
das.writeByte( nPlayersH );
das.flush();
send( context, NBS_CMS.NBS_CMD_INVITE, bas.toByteArray(), phone );
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
}
private static void send( Context context, NBS_CMS cmd,
byte[] data, String phone )
{
int hash = Arrays.hashCode( data );
DbgUtils.logf( "NBSReceiver: outgoing hash on %d bytes: %X",
data.length, hash );
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
DataOutputStream das = new DataOutputStream( bas );
try {
das.writeByte( cmd.ordinal() );
das.writeInt( hash );
das.write( data, 0, data.length );
das.flush();
byte[] as64 = Base64.encode( bas.toByteArray(), Base64.NO_WRAP );
sendBuffer( context, as64, phone );
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
}
private void receive( Context context, NBS_CMS cmd,
byte[] data, String phone )
{
switch( cmd ) {
case NBS_CMD_INVITE:
DataInputStream dis =
new DataInputStream( new ByteArrayInputStream(data) );
try {
int gameID = dis.readInt();
String gameName = dis.readUTF();
int lang = dis.readInt();
int nPlayersT = dis.readByte();
int nPlayersH = dis.readByte();
CommsAddrRec addr = new CommsAddrRec( phone );
long rowid = GameUtils.makeNewNBSGame( context, gameID, addr,
lang, nPlayersT, nPlayersH );
if ( null != gameName && 0 < gameName.length() ) {
DBUtils.setName( context, rowid, gameName );
}
String body = Utils.format( context, R.string.new_nbs_bodyf, phone );
postNotification( context, gameID, R.string.new_nbs_title, body );
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
break;
default:
Assert.fail();
break;
}
}
private void receiveBuffer( Context context, byte[] as64, String senderPhone )
{
byte[] data = Base64.decode( as64, Base64.NO_WRAP );
DataInputStream dis =
new DataInputStream( new ByteArrayInputStream(data) );
try {
NBS_CMS cmd = NBS_CMS.values()[dis.readByte()];
int hashRead = dis.readInt();
DbgUtils.logf( "NBSReceiver: incoming hash: %X", hashRead );
byte[] rest = new byte[dis.available()];
dis.read( rest );
int hashComputed = Arrays.hashCode( rest );
if ( hashComputed == hashRead ) {
DbgUtils.logf( "NBSReceiver: incoming hashes on %d bytes match: %X",
rest.length, hashRead );
receive( context, cmd, rest, senderPhone );
} else {
DbgUtils.logf( "NBSReceiver: incoming hashes on %d bytes "
+ "DON'T match: read: %X; figured: %X",
rest.length, hashRead, hashComputed );
}
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
}
private static void sendBuffer( Context context, byte[] data, String phone )
{
if ( XWApp.onEmulator() ) {
DbgUtils.logf( "sendBuffer(phone=%s): FAKING IT", phone );
} else {
try {
PendingIntent sent =
PendingIntent.getBroadcast( context, 0, new Intent(), 0 );
PendingIntent dlvrd =
PendingIntent.getBroadcast( context, 0, new Intent(), 0 );
SmsManager mgr = SmsManager.getDefault();
mgr.sendDataMessage( phone, null, XWApp.getNBSPort(),
data, sent, dlvrd );
// PendingIntent sentIntent,
// PendingIntent deliveryIntent );
DbgUtils.logf( "send to %s finished", phone );
} catch ( IllegalArgumentException iae ) {
DbgUtils.logf( "%s", iae.toString() );
}
}
}
private void postNotification( Context context, int gameID, int title,
String body )
{
Intent intent = new Intent( context, DispatchNotify.class );
intent.putExtra( DispatchNotify.GAMEID_EXTRA, gameID );
Utils.postNotification( context, intent, R.string.new_nbsmove_title,
body );
}
}

View file

@ -0,0 +1,492 @@
/* -*- compile-command: "cd ../../../../../; ant debug install"; -*- */
/*
* Copyright 2010 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.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Base64;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.lang.System;
import junit.framework.Assert;
import org.eehouse.android.xw4.jni.CommsAddrRec;
public class NBSService extends Service {
private static final int MAX_LEN_TEXT = 100;
private static final int HANDLE = 1;
private static final int INVITE = 2;
private static final int SEND = 3;
private static final String CMD_STR = "CMD";
private static final String BUFFER = "BUFFER";
private static final String PHONE = "PHONE";
private static final String GAMEID = "GAMEID";
private static final String GAMENAME = "GAMENAME";
private static final String LANG = "LANG";
private static final String NPLAYERST = "NPLAYERST";
private static final String NPLAYERSH = "NPLAYERSH";
// All messages are base64-encoded byte arrays. The first byte is
// always one of these. What follows depends.
private enum NBS_CMD { NONE, INVITE, DATA, };
private int m_nReceived = 0;
private static int s_nSent = 0;
private static HashMap<String, HashMap <Integer, MsgStore>> s_partialMsgs
= new HashMap<String, HashMap <Integer, MsgStore>>();
public static void handleFrom( Context context, byte[] buffer, String phone )
{
Intent intent = getIntentTo( context, HANDLE );
intent.putExtra( BUFFER, buffer );
intent.putExtra( PHONE, phone );
context.startService( intent );
}
public static void inviteRemote( Context context, String phone,
int gameID, String gameName,
int lang, int nPlayersT,
int nPlayersH )
{
DbgUtils.logf( "inviteRemote(%s)", phone );
Intent intent = getIntentTo( context, INVITE );
intent.putExtra( PHONE, phone );
intent.putExtra( GAMEID, gameID );
intent.putExtra( GAMENAME, gameName );
intent.putExtra( LANG, lang );
intent.putExtra( NPLAYERST, nPlayersT );
intent.putExtra( NPLAYERSH, nPlayersH );
context.startService( intent );
}
public static int sendPacket( Context context, String phone,
int gameID, byte[] buffer )
{
DbgUtils.logf( "NBSService.sendPacket()" );
Intent intent = getIntentTo( context, SEND );
intent.putExtra( PHONE, phone );
intent.putExtra( GAMEID, gameID );
intent.putExtra( BUFFER, buffer );
context.startService( intent );
return buffer.length;
}
private static Intent getIntentTo( Context context, int cmd )
{
Intent intent = new Intent( context, NBSService.class );
intent.putExtra( CMD_STR, cmd );
return intent;
}
@Override
public void onCreate()
{
if ( XWApp.NBSSUPPORTED ) {
} else {
stopSelf();
}
}
@Override
public int onStartCommand( Intent intent, int flags, int startId )
{
int result;
if ( XWApp.NBSSUPPORTED ) {
int cmd = intent.getIntExtra( CMD_STR, -1 );
switch( cmd ) {
case HANDLE:
DbgUtils.showf( this, "got %dth nbs", ++m_nReceived );
byte[] buffer = intent.getByteArrayExtra( BUFFER );
String phone = intent.getStringExtra( PHONE );
receiveBuffer( buffer, phone );
break;
case INVITE:
phone = intent.getStringExtra( PHONE );
DbgUtils.logf( "INVITE(%s)", phone );
int gameID = intent.getIntExtra( GAMEID, -1 );
String gameName = intent.getStringExtra( GAMENAME );
int lang = intent.getIntExtra( LANG, -1 );
int nPlayersT = intent.getIntExtra( NPLAYERST, -1 );
int nPlayersH = intent.getIntExtra( NPLAYERSH, -1 );
inviteRemote( phone, gameID, gameName, lang, nPlayersT,
nPlayersH);
break;
case SEND:
phone = intent.getStringExtra( PHONE );
buffer = intent.getByteArrayExtra( BUFFER );
gameID = intent.getIntExtra( GAMEID, -1 );
sendPacket( phone, gameID, buffer );
break;
}
result = Service.START_STICKY;
} else {
result = Service.START_STICKY_COMPATIBILITY;
}
return result;
}
@Override
public IBinder onBind( Intent intent )
{
return null;
}
private void inviteRemote( String phone, int gameID, String gameName,
int lang, int nPlayersT, int nPlayersH )
{
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
DataOutputStream das = new DataOutputStream( bas );
try {
das.writeInt( gameID );
das.writeUTF( gameName );
das.writeInt( lang );
das.writeByte( nPlayersT );
das.writeByte( nPlayersH );
das.flush();
send( NBS_CMD.INVITE, bas.toByteArray(), phone );
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
}
public int sendPacket( String phone, int gameID, byte[] buf )
{
DbgUtils.logf( "non-static NBSService.sendPacket()" );
int nSent = -1;
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
DataOutputStream das = new DataOutputStream( bas );
try {
das.writeInt( gameID );
das.write( buf, 0, buf.length );
das.flush();
if ( send( NBS_CMD.DATA, bas.toByteArray(), phone ) ) {
nSent = buf.length;
}
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
return nSent;
}
private boolean send( NBS_CMD cmd, byte[] data, String phone )
throws java.io.IOException
{
DbgUtils.logf( "non-static NBSService.sendPacket()" );
int hash = Arrays.hashCode( data );
DbgUtils.logf( "NBSService: outgoing hash on %d bytes: %X",
data.length, hash );
ByteArrayOutputStream bas = new ByteArrayOutputStream( 128 );
DataOutputStream das = new DataOutputStream( bas );
das.writeByte( 0 ); // protocol
das.writeByte( cmd.ordinal() );
das.writeInt( hash );
das.write( data, 0, data.length );
das.flush();
String as64 = Base64.encodeToString( bas.toByteArray(), Base64.NO_WRAP );
byte[][] msgs = breakAndEncode( as64 );
return sendBuffers( msgs, phone );
}
private byte[][] breakAndEncode( String msg )
throws java.io.IOException
{
// TODO: as optimization, truncate header when only one packet
// required
Assert.assertFalse( msg.contains(":") );
int count = (msg.length() + (MAX_LEN_TEXT-1)) / MAX_LEN_TEXT;
byte[][] result = new byte[count][];
int msgID = ++s_nSent % 0x000000FF;
DbgUtils.logf( "preparing %d packets for msgid %x", count, msgID );
int start = 0;
int end = 0;
for ( int ii = 0; ii < count; ++ii ) {
int len = msg.length() - end;
if ( len > MAX_LEN_TEXT ) {
len = MAX_LEN_TEXT;
}
end += len;
String out = String.format( "0:%X:%X:%X:%s", msgID, ii, count,
msg.substring( start, end ) );
DbgUtils.logf( "fragment[%d]: %s", ii, out );
result[ii] = out.getBytes();
start = end;
}
return result;
}
private void receive( NBS_CMD cmd, byte[] data, String phone )
{
CommsAddrRec addr = new CommsAddrRec( phone );
DataInputStream dis =
new DataInputStream( new ByteArrayInputStream(data) );
try {
switch( cmd ) {
case INVITE:
int gameID = dis.readInt();
String gameName = dis.readUTF();
int lang = dis.readInt();
int nPlayersT = dis.readByte();
int nPlayersH = dis.readByte();
long rowid = GameUtils.makeNewNBSGame( this, gameID, addr,
lang, nPlayersT, nPlayersH );
if ( null != gameName && 0 < gameName.length() ) {
DBUtils.setName( this, rowid, gameName );
}
String body = Utils.format( this, R.string.new_nbs_bodyf, phone );
postNotification( gameID, R.string.new_nbs_title, body );
break;
case DATA:
gameID = dis.readInt();
byte[] rest = new byte[dis.available()];
dis.read( rest );
feedMessage( gameID, rest, addr );
break;
default:
Assert.fail();
break;
}
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
}
private void receiveBuffer( byte[] as64, String senderPhone )
{
String asString = new String( as64 );
DbgUtils.logf( "receiveBuffer(%s)", asString );
String[] parts = asString.split( ":" );
DbgUtils.logf( "receiveBuffer: got %d parts", parts.length );
for ( String part : parts ) {
DbgUtils.logf( "part: %s", part );
}
Assert.assertTrue( 5 == parts.length );
if ( 5 == parts.length ) {
byte proto = Byte.valueOf( parts[0], 10 );
int id = Integer.valueOf( parts[1], 16 );
int index = Integer.valueOf( parts[2], 16 );
int count = Integer.valueOf( parts[3], 16 );
tryAssemble( senderPhone, id, index, count, parts[4] );
}
}
private void tryAssemble( String senderPhone, int id, int index,
int count, String msg )
{
if ( index == 0 && count == 1 ) {
disAssemble( senderPhone, msg.getBytes() );
} else {
synchronized( s_partialMsgs ) {
HashMap <Integer, MsgStore> perPhone =
s_partialMsgs.get( senderPhone );
if ( null == perPhone ) {
perPhone = new HashMap <Integer, MsgStore>();
s_partialMsgs.put( senderPhone, perPhone );
}
MsgStore store = perPhone.get( id );
if ( null == store ) {
store = new MsgStore( id, count );
perPhone.put( id, store );
}
store.add( index, msg );
if ( store.isComplete() ) {
byte[] fullMsg = store.message();
perPhone.remove( id );
disAssemble( senderPhone, fullMsg );
}
}
}
}
private void disAssemble( String senderPhone, byte[] fullMsg )
{
DbgUtils.logf( "disAssemble()" );
byte[] data = Base64.decode( fullMsg, Base64.NO_WRAP );
DataInputStream dis =
new DataInputStream( new ByteArrayInputStream(data) );
try {
byte proto = dis.readByte();
if ( 0 != proto ) {
DbgUtils.logf( "NBSService.disAssemble: bad proto %d; dropping",
proto );
} else {
NBS_CMD cmd = NBS_CMD.values()[dis.readByte()];
int hashRead = dis.readInt();
DbgUtils.logf( "NBSService: incoming hash: %X", hashRead );
byte[] rest = new byte[dis.available()];
dis.read( rest );
int hashComputed = Arrays.hashCode( rest );
if ( hashComputed == hashRead ) {
DbgUtils.logf( "NBSService: incoming hashes on %d " +
"bytes match: %X", rest.length, hashRead );
receive( cmd, rest, senderPhone );
} else {
DbgUtils.logf( "NBSService: incoming hashes on %d bytes "
+ "DON'T match: read: %X; figured: %X",
rest.length, hashRead, hashComputed );
}
}
} catch ( java.io.IOException ioe ) {
DbgUtils.logf( "ioe: %s", ioe.toString() );
}
}
private boolean sendBuffers( byte[][] fragments, String phone )
{
DbgUtils.logf( "NBSService.sendBuffers()" );
boolean success = false;
if ( XWApp.onEmulator() ) {
DbgUtils.logf( "sendBuffer(phone=%s): FAKING IT", phone );
} else {
try {
PendingIntent sent =
PendingIntent.getBroadcast( this, 0, new Intent(), 0 );
PendingIntent dlvrd =
PendingIntent.getBroadcast( this, 0, new Intent(), 0 );
SmsManager mgr = SmsManager.getDefault();
short port = XWApp.getNBSPort();
for ( byte[] fragment : fragments ) {
String tmp = new String(fragment);
DbgUtils.logf( "sending len %d packet: %s", tmp.length(), tmp );
mgr.sendDataMessage( phone, null, port, fragment, sent, dlvrd );
DbgUtils.logf( "sendDataMessage of %d bytes to %s on %d "
+ "finished", fragment.length, phone, port );
}
DbgUtils.showf( this, "send %dth msg", ++s_nSent );
success = true;
} catch ( IllegalArgumentException iae ) {
DbgUtils.logf( "%s", iae.toString() );
} catch ( Exception ee ) {
DbgUtils.logf( "sendDataMessage message failed: %s",
ee.toString() );
}
}
return success;
}
private void feedMessage( int gameID, byte[] msg, CommsAddrRec addr )
{
if ( BoardActivity.feedMessage( gameID, msg, addr ) ) {
// do nothing
} else {
long rowid = DBUtils.getRowIDFor( this, gameID );
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
NBSMsgSink sink = new NBSMsgSink( this );
if ( GameUtils.feedMessage( this, rowid, msg, addr, sink ) ) {
postNotification( gameID, R.string.new_nbsmove_title,
getString(R.string.new_move_body)
);
}
}
}
}
private void postNotification( int gameID, int title, String body )
{
Intent intent = new Intent( this, DispatchNotify.class );
intent.putExtra( DispatchNotify.GAMEID_EXTRA, gameID );
Utils.postNotification( this, intent, R.string.new_nbsmove_title,
body );
}
private class NBSMsgSink extends MultiMsgSink {
private Context m_context;
public NBSMsgSink( Context context ) {
super();
m_context = context;
}
/***** TransportProcs interface *****/
public int transportSend( byte[] buf, final CommsAddrRec addr, int gameID )
{
DbgUtils.logf( "NBSMsgSink.transportSend()" );
return sendPacket( addr.sms_phone, gameID, buf );
}
public boolean relayNoConnProc( byte[] buf, String relayID )
{
Assert.fail();
return false;
}
}
private class MsgStore {
String[] m_msgs;
int m_msgID;
int m_haveCount;
int m_fullLength;
public MsgStore( int id, int count )
{
m_msgID = id;
m_msgs = new String[count];
m_fullLength = 0;
}
public void add( int index, String msg )
{
if ( null == m_msgs[index] ) {
++m_haveCount;
m_fullLength += msg.length();
}
m_msgs[index] = msg;
}
public boolean isComplete()
{
boolean complete = m_msgs.length == m_haveCount;
DbgUtils.logf( "isComplete(msg %d)=>%b", m_msgID, complete );
return complete;
}
public byte[] message()
{
byte[] result = new byte[m_fullLength];
int offset = 0;
for ( int ii = 0; ii < m_msgs.length; ++ii ) {
byte[] src = m_msgs[ii].getBytes();
System.arraycopy( src, 0, result, offset, src.length );
offset += src.length;
}
return result;
}
}
}

View file

@ -193,7 +193,7 @@ public class NewGameActivity extends XWActivity {
m_gameID = GameUtils.newGameID();
m_gameName = Utils.format( this, R.string.dft_nbs_namef,
m_gameID & 0xFFFF );
NBSReceiver.inviteRemote( NewGameActivity.this, phones[0],
NBSService.inviteRemote( NewGameActivity.this, phones[0],
m_gameID, m_gameName,
m_lang, 2, 1 );
long rowid =
@ -355,6 +355,10 @@ public class NewGameActivity extends XWActivity {
m_newRowID = GameUtils.makeNewNBSGame( NewGameActivity.this,
gameID, null, m_lang,
2, 1 ); // initial defaults
String name = Utils.format( this, R.string.dft_nbs_namef,
gameID & 0xFFFF );
DBUtils.setName( this, m_newRowID, name );
Intent intent = new Intent( this, GameConfig.class );
intent.setAction( Intent.ACTION_EDIT );
intent.putExtra( GameUtils.INTENT_KEY_ROWID, m_newRowID );