add choice of SMS and BT as transports. SMS can't be tested on

emulator and so isn't; BT isn't implemented at all.  But SMS works as
far as the send proc.
This commit is contained in:
eehouse 2010-02-09 14:43:37 +00:00
parent 37646c20f3
commit 099cf66a79
6 changed files with 325 additions and 82 deletions

View file

@ -141,7 +141,7 @@ public class BoardActivity extends Activity implements UtilCtxt {
m_jniGamePtr = XwJNI.initJNI();
if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) {
m_xport = new CommsTransport( m_jniGamePtr );
m_xport = new CommsTransport( m_jniGamePtr, this );
}
if ( null == stream ||
@ -193,6 +193,9 @@ public class BoardActivity extends Activity implements UtilCtxt {
protected void onDestroy()
{
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, m_gi );
Utils.saveGame( this, state, m_path );
if ( null != m_xport ) {
m_xport.waitToStop();
}
@ -202,9 +205,6 @@ public class BoardActivity extends Activity implements UtilCtxt {
m_jniThread.waitToStop();
Utils.logf( "onDestroy(): waitToStop() returned" );
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, m_gi );
Utils.saveGame( this, state, m_path );
XwJNI.game_dispose( m_jniGamePtr );
m_jniGamePtr = 0;
}

View file

@ -1,4 +1,4 @@
/* -*- compile-command: "cd ../../../../../; ant reinstall"; -*- */
/* -*- compile-command: "cd ../../../../../; ant install"; -*- */
package org.eehouse.android.xw4;
@ -12,9 +12,14 @@ import java.net.InetSocketAddress;
import java.util.Vector;
import java.util.Iterator;
import junit.framework.Assert;
import android.telephony.SmsManager;
import android.content.Intent;
import android.app.PendingIntent;
import android.content.Context;
import org.eehouse.android.xw4.jni.*;
import org.eehouse.android.xw4.jni.JNIThread.*;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
public class CommsTransport extends Thread implements TransportProcs {
private Selector m_selector;
@ -29,13 +34,17 @@ public class CommsTransport extends Thread implements TransportProcs {
private ByteBuffer m_bytesOut;
private ByteBuffer m_bytesIn;
private Context m_context;
// assembling inbound packet
private byte[] m_packetIn;
private int m_haveLen = -1;
public CommsTransport( int jniGamePtr )
public CommsTransport( int jniGamePtr, Context context )
{
m_jniGamePtr = jniGamePtr;
m_context = context;
m_buffersOut = new Vector<ByteBuffer>();
m_bytesIn = ByteBuffer.allocate( 2048 );
}
@ -48,11 +57,15 @@ public class CommsTransport extends Thread implements TransportProcs {
public void waitToStop()
{
m_done = true;
m_selector.wakeup();
try {
join(100); // wait up to 1/10 second
} catch ( java.lang.InterruptedException ie ) {
Utils.logf( "got InterruptedException: " + ie.toString() );
if ( null != m_selector ) {
m_selector.wakeup();
}
if ( m_running ) { // synchronized this? Or use Thread method
try {
join(100); // wait up to 1/10 second
} catch ( java.lang.InterruptedException ie ) {
Utils.logf( "got InterruptedException: " + ie.toString() );
}
}
}
@ -213,6 +226,7 @@ public class CommsTransport extends Thread implements TransportProcs {
// TransportProcs interface
public int transportSend( byte[] buf, final CommsAddrRec faddr )
{
int nSent = -1;
Utils.logf( "CommsTransport::transportSend" );
if ( null == m_addr ) {
@ -224,20 +238,42 @@ public class CommsTransport extends Thread implements TransportProcs {
}
}
// add this packet to queue
putOut( buf );
switch ( m_addr.conType ) {
case COMMS_CONN_RELAY:
putOut( buf ); // add to queue
if ( !m_running ) {
m_running = true;
start();
}
nSent = buf.length;
break;
case COMMS_CONN_SMS:
Utils.logf( "sending via sms to " + m_addr.sms_phone + ":127" );
// start the read/write thread. Note: server needs to start
// before first asked to send. For relay, though, that'll
// happen. Not sure about other transport e.g. BT where
// server needs to get into listening mode.
if ( !m_running ) {
m_running = true;
start();
try {
Intent intent = new Intent( m_context, StatusReceiver.class);
PendingIntent pi
= PendingIntent.getBroadcast( m_context, 0,
intent, 0 );
// SmsManager.getDefault().sendTextMessage( m_addr.sms_phone,
// null, "Hello world",
// pi, pi );
SmsManager.getDefault().sendDataMessage( m_addr.sms_phone,
(String)null, (short)127,
//(short)m_addr.sms_port,
buf, pi, pi );
Utils.logf( "called sendDataMessage" );
nSent = buf.length;
} catch ( java.lang.IllegalArgumentException iae ) {
Utils.logf( iae.toString() );
}
break;
case COMMS_CONN_BT:
break;
}
return buf.length;
}
return nSent;
}
public void relayStatus( int newState )
{

View file

@ -62,19 +62,16 @@ import android.widget.ArrayAdapter;
import android.webkit.WebView;
import java.io.File;
import android.widget.LinearLayout;
import junit.framework.Assert;
import org.eehouse.android.xw4.jni.*;
/**
* A generic activity for editing a note in a database. This can be used
* either to simply view a note {@link Intent#ACTION_VIEW}, view and edit a note
* {@link Intent#ACTION_EDIT}, or create a new note {@link Intent#ACTION_INSERT}.
*/
public class GameConfig extends Activity implements View.OnClickListener {
private static final int PLAYER_EDIT = 1;
private static final int ROLE_EDIT = 2;
private static final int ROLE_EDIT_RELAY = 2;
private static final int ROLE_EDIT_SMS = 3;
private static final int ROLE_EDIT_BT = 4;
private Button m_addPlayerButton;
private Button m_configureButton;
@ -82,13 +79,16 @@ public class GameConfig extends Activity implements View.OnClickListener {
private CurGameInfo m_gi;
private int m_whichPlayer;
private Dialog m_curDialog;
private Spinner m_dictSpinner;
private Spinner m_roleSpinner;
private Spinner m_connectSpinner;
private Spinner m_phoniesSpinner;
private Spinner m_dictSpinner;
private String[] m_dicts;
private int m_browsePosition;
private LinearLayout m_playerLayout;
private CommsAddrRec m_car;
private int m_connLayoutID;
private CommonPrefs m_cp;
@Override
protected Dialog onCreateDialog( int id )
@ -116,10 +116,12 @@ public class GameConfig extends Activity implements View.OnClickListener {
.setView(playerEditView)
.setPositiveButton(R.string.button_save, dlpos )
.create();
case ROLE_EDIT:
case ROLE_EDIT_RELAY:
case ROLE_EDIT_SMS:
case ROLE_EDIT_BT:
factory = LayoutInflater.from(this);
final View roleEditView
= factory.inflate( R.layout.role_edit, null );
= factory.inflate( layoutForDlg(id), null );
dlpos = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
@ -130,7 +132,7 @@ public class GameConfig extends Activity implements View.OnClickListener {
return new AlertDialog.Builder( this )
// .setIcon(R.drawable.alert_dialog_icon)
.setTitle(R.string.role_edit_title)
.setTitle(titleForDlg(id))
.setView(roleEditView)
.setPositiveButton(R.string.button_save, dlpos )
.create();
@ -142,11 +144,14 @@ public class GameConfig extends Activity implements View.OnClickListener {
protected void onPrepareDialog( int id, Dialog dialog )
{
m_curDialog = dialog;
switch ( id ) {
case PLAYER_EDIT:
setPlayerSettings();
break;
case ROLE_EDIT:
case ROLE_EDIT_RELAY:
case ROLE_EDIT_SMS:
case ROLE_EDIT_BT:
setRoleSettings();
break;
}
@ -166,21 +171,43 @@ public class GameConfig extends Activity implements View.OnClickListener {
private void setRoleSettings()
{
if ( null == m_car ) {
m_car = new CommsAddrRec( CommsAddrRec.get() );
int position = m_connectSpinner.getSelectedItemPosition();
switch( posToConnType( position ) ) {
case COMMS_CONN_RELAY:
Utils.setText( m_curDialog, R.id.room_edit, m_car.ip_relay_invite );
Utils.setText( m_curDialog, R.id.hostname_edit,
m_car.ip_relay_hostName );
Utils.setInt( m_curDialog, R.id.port_edit, m_car.ip_relay_port );
break;
case COMMS_CONN_SMS:
Utils.setText( m_curDialog, R.id.sms_phone_edit, m_car.sms_phone );
Utils.logf( "set phone: " + m_car.sms_phone );
Utils.setInt( m_curDialog, R.id.sms_port_edit, m_car.sms_port );
break;
case COMMS_CONN_BT:
}
Utils.setText( m_curDialog, R.id.room_edit, m_car.ip_relay_invite );
Utils.setText( m_curDialog, R.id.hostname_edit,
m_car.ip_relay_hostName );
Utils.setInt( m_curDialog, R.id.port_edit, m_car.ip_relay_port );
}
private void getRoleSettings()
{
m_car.ip_relay_invite = Utils.getText( m_curDialog, R.id.room_edit );
m_car.ip_relay_hostName = Utils.getText( m_curDialog,
R.id.hostname_edit );
m_car.ip_relay_port = Utils.getInt( m_curDialog, R.id.port_edit );
int position = m_connectSpinner.getSelectedItemPosition();
m_car.conType = posToConnType( position );
switch ( m_car.conType ) {
case COMMS_CONN_RELAY:
m_car.ip_relay_invite = Utils.getText( m_curDialog, R.id.room_edit );
m_car.ip_relay_hostName = Utils.getText( m_curDialog,
R.id.hostname_edit );
m_car.ip_relay_port = Utils.getInt( m_curDialog, R.id.port_edit );
break;
case COMMS_CONN_SMS:
m_car.sms_phone = Utils.getText( m_curDialog, R.id.sms_phone_edit );
Utils.logf( "grabbed phone: " + m_car.sms_phone );
m_car.sms_port = (short)Utils.getInt( m_curDialog,
R.id.sms_port_edit );
break;
case COMMS_CONN_BT:
break;
}
}
private void getPlayerSettings()
@ -199,6 +226,8 @@ public class GameConfig extends Activity implements View.OnClickListener {
{
super.onCreate(savedInstanceState);
m_cp = CommonPrefs.get();
Intent intent = getIntent();
Uri uri = intent.getData();
m_path = uri.getPath();
@ -209,8 +238,24 @@ public class GameConfig extends Activity implements View.OnClickListener {
byte[] stream = Utils.savedGame( this, m_path );
m_gi = new CurGameInfo( this );
XwJNI.gi_from_stream( m_gi, stream );
byte[] dictBytes = Utils.openDict( this, m_gi.dictName );
int gamePtr = XwJNI.initJNI();
if ( !XwJNI.game_makeFromStream( gamePtr, stream, m_gi, dictBytes,
m_cp ) ) {
XwJNI.game_makeNewGame( gamePtr, m_gi, m_cp, dictBytes );
}
int curSel = listAvailableDicts( m_gi.dictName );
m_car = new CommsAddrRec();
if ( XwJNI.game_hasComms( gamePtr ) ) {
XwJNI.comms_getAddr( gamePtr, m_car );
} else {
XwJNI.comms_getInitialAddr( m_car );
}
XwJNI.game_dispose( gamePtr );
setContentView(R.layout.game_config);
m_addPlayerButton = (Button)findViewById(R.id.add_player);
@ -260,9 +305,24 @@ public class GameConfig extends Activity implements View.OnClickListener {
getString(R.string.role_host),
getString(R.string.role_guest),
} );
adapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
adapter.setDropDownViewResource( android.R.layout
.simple_spinner_dropdown_item );
m_roleSpinner.setAdapter( adapter );
m_roleSpinner.setSelection( m_gi.serverRole.ordinal() );
m_roleSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parentView,
View selectedItemView, int position,
long id ) {
adjustVisibility( position );
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {
}
});
configConnectSpinner();
m_phoniesSpinner = (Spinner)findViewById( R.id.phonies_spinner );
adapter =
@ -273,7 +333,8 @@ public class GameConfig extends Activity implements View.OnClickListener {
getString(R.string.phonies_warn),
getString(R.string.phonies_disallow),
} );
adapter.setDropDownViewResource( android.R.layout.simple_spinner_dropdown_item );
adapter.setDropDownViewResource( android.R.layout
.simple_spinner_dropdown_item );
m_phoniesSpinner.setAdapter( adapter );
m_phoniesSpinner.setSelection( m_gi.phoniesAction.ordinal() );
@ -281,8 +342,17 @@ public class GameConfig extends Activity implements View.OnClickListener {
Utils.setChecked( this, R.id.use_timer, m_gi.timerEnabled );
Utils.setChecked( this, R.id.color_tiles, m_gi.showColors );
Utils.setChecked( this, R.id.smart_robot, 0 < m_gi.robotSmartness );
adjustVisibility(-1);
} // onCreate
@Override
protected void onPause()
{
saveChanges();
super.onPause(); // skip this and get a crash :-)
}
@Override
public void onCreateContextMenu( ContextMenu menu, View view,
ContextMenuInfo menuInfo ) {
@ -339,29 +409,8 @@ public class GameConfig extends Activity implements View.OnClickListener {
m_gi.juggle();
loadPlayers();
break;
case R.id.game_config_done:
m_gi.hintsNotAllowed = !Utils.getChecked( this, R.id.hints_allowed );
m_gi.timerEnabled = Utils.getChecked( this, R.id.use_timer );
m_gi.showColors = Utils.getChecked( this, R.id.color_tiles );
m_gi.robotSmartness
= Utils.getChecked( this, R.id.smart_robot ) ? 1 : 0;
int position = m_roleSpinner.getSelectedItemPosition();
Utils.logf( "setting serverrole: " + position );
m_gi.serverRole = CurGameInfo.DeviceRole.values()[position];
byte[] bytes = XwJNI.gi_to_stream( m_gi );
if ( null == bytes ) {
Utils.logf( "gi_to_stream failed" );
} else {
Utils.logf( "got " + bytes.length + " bytes." );
Utils.saveGame( this, bytes, m_path );
}
if ( null != m_car ) {
CommsAddrRec.set( m_car );
}
finish();
case R.id.game_config_revert:
Utils.notImpl( this );
break;
default:
handled = false;
@ -380,7 +429,18 @@ public class GameConfig extends Activity implements View.OnClickListener {
showDialog( PLAYER_EDIT );
}
} else if ( m_configureButton == view ) {
showDialog( ROLE_EDIT );
int position = m_connectSpinner.getSelectedItemPosition();
switch ( posToConnType( position ) ) {
case COMMS_CONN_RELAY:
showDialog( ROLE_EDIT_RELAY );
break;
case COMMS_CONN_SMS:
showDialog( ROLE_EDIT_SMS );
break;
case COMMS_CONN_BT:
showDialog( ROLE_EDIT_BT );
break;
}
} else {
Utils.logf( "unknown v: " + view.toString() );
}
@ -443,4 +503,127 @@ public class GameConfig extends Activity implements View.OnClickListener {
Intent intent = new Intent( this, DictActivity.class );
startActivity( intent );
}
private void configConnectSpinner()
{
m_connectSpinner = (Spinner)findViewById( R.id.connect_spinner );
ArrayAdapter<String> adapter =
new ArrayAdapter<String>( this,
android.R.layout.simple_spinner_item,
new String[] {
getString(R.string.tab_relay),
getString(R.string.tab_sms),
getString(R.string.tab_bluetooth),
} );
adapter.setDropDownViewResource( android.R.layout
.simple_spinner_dropdown_item );
m_connectSpinner.setAdapter( adapter );
m_connectSpinner.setSelection( connTypeToPos( m_car.conType ) );
} // configConnectSpinner
private void adjustVisibility( int position )
{
int[] ids = { R.id.connect_via_label,
R.id.connect_spinner,
R.id.configure_role };
if ( position == -1 ) {
position = m_roleSpinner.getSelectedItemPosition();
}
int vis = 0 == position ? View.GONE : View.VISIBLE;
for ( int id : ids ) {
View view = findViewById( id );
view.setVisibility( vis );
}
}
private int connTypeToPos( CommsAddrRec.CommsConnType typ )
{
switch( typ ) {
case COMMS_CONN_RELAY:
return 0;
case COMMS_CONN_SMS:
return 1;
case COMMS_CONN_BT:
return 2;
}
return -1;
}
private int layoutForDlg( int id )
{
switch( id ) {
case ROLE_EDIT_RELAY:
return R.layout.role_edit_relay;
case ROLE_EDIT_SMS:
return R.layout.role_edit_sms;
case ROLE_EDIT_BT:
return R.layout.role_edit_bt;
}
Assert.fail();
return 0;
}
private int titleForDlg( int id )
{
switch( id ) {
case ROLE_EDIT_RELAY:
return R.string.tab_relay;
case ROLE_EDIT_SMS:
return R.string.tab_sms;
case ROLE_EDIT_BT:
return R.string.tab_bluetooth;
}
Assert.fail();
return -1;
}
private CommsAddrRec.CommsConnType posToConnType( int position )
{
switch( position ) {
case 0:
return CommsAddrRec.CommsConnType.COMMS_CONN_RELAY;
case 1:
return CommsAddrRec.CommsConnType.COMMS_CONN_SMS;
case 2:
return CommsAddrRec.CommsConnType.COMMS_CONN_BT;
default:
Assert.fail();
break;
}
return CommsAddrRec.CommsConnType.COMMS_CONN_NONE;
}
private void saveChanges()
{
m_gi.hintsNotAllowed = !Utils.getChecked( this, R.id.hints_allowed );
m_gi.timerEnabled = Utils.getChecked( this, R.id.use_timer );
m_gi.showColors = Utils.getChecked( this, R.id.color_tiles );
m_gi.robotSmartness
= Utils.getChecked( this, R.id.smart_robot ) ? 1 : 0;
int position = m_roleSpinner.getSelectedItemPosition();
Utils.logf( "setting serverrole: " + position );
m_gi.serverRole = CurGameInfo.DeviceRole.values()[position];
position = m_phoniesSpinner.getSelectedItemPosition();
m_gi.phoniesAction = CurGameInfo.XWPhoniesChoice.values()[position];
position = m_connectSpinner.getSelectedItemPosition();
m_car.conType = posToConnType( position );
byte[] dictBytes = Utils.openDict( this, m_gi.dictName );
int gamePtr = XwJNI.initJNI();
XwJNI.game_makeNewGame( gamePtr, m_gi, m_cp, dictBytes );
if ( null != m_car ) {
XwJNI.comms_setAddr( gamePtr, m_car );
}
byte[] stream = XwJNI.game_saveToStream( gamePtr, m_gi );
Utils.saveGame( this, stream, m_path );
XwJNI.game_dispose( gamePtr );
}
}

View file

@ -218,12 +218,26 @@ public class Utils {
}
}
public static void setText( Activity activity, int id, String value )
{
EditText editText = (EditText)activity.findViewById( id );
if ( null != editText ) {
editText.setText( value, TextView.BufferType.EDITABLE );
}
}
public static void setInt( Dialog dialog, int id, int value )
{
String str = Integer.toString(value);
setText( dialog, id, str );
}
public static void setInt( Activity activity, int id, int value )
{
String str = Integer.toString(value);
setText( activity, id, str );
}
public static boolean getChecked( Activity activity, int id )
{
CheckBox cbx = (CheckBox)activity.findViewById( id );
@ -242,10 +256,29 @@ public class Utils {
return editText.getText().toString();
}
public static String getText( Activity activity, int id )
{
EditText editText = (EditText)activity.findViewById( id );
return editText.getText().toString();
}
public static int getInt( Dialog dialog, int id )
{
String str = getText( dialog, id );
return Integer.parseInt( str );
try {
return Integer.parseInt( str );
} catch ( NumberFormatException nfe ) {
return 0;
}
}
public static int getInt( Activity activity, int id )
{
String str = getText( activity, id );
try {
return Integer.parseInt( str );
} catch ( NumberFormatException nfe ) {
return 0;
}
}
}

View file

@ -1,7 +1,8 @@
/* -*- compile-command: "cd ../../../../../../; ant reinstall"; -*- */
/* -*- compile-command: "cd ../../../../../../; ant install"; -*- */
package org.eehouse.android.xw4.jni;
import java.net.InetAddress;
import android.content.Context;
import org.eehouse.android.xw4.Utils;
@ -58,13 +59,4 @@ public class CommsAddrRec {
}
return s_car;
}
public static void set( final CommsAddrRec car )
{
if ( null == s_car ) {
s_car = new CommsAddrRec( car );
} else {
s_car.copyFrom( car );
}
}
}

View file

@ -176,7 +176,6 @@ public class JNIThread extends Thread {
break;
case CMD_START:
XwJNI.comms_setAddr( m_jniGamePtr, CommsAddrRec.get() );
XwJNI.comms_start( m_jniGamePtr );
if ( m_gi.serverRole == DeviceRole.SERVER_ISCLIENT ) {
XwJNI.server_initClientConnection( m_jniGamePtr );