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 d12c4deac..a11b686f5 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -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; } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java index b15cec0aa..252a2267a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/CommsTransport.java @@ -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(); 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 ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java index ea2fa3a44..72937c977 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java @@ -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 adapter = + new ArrayAdapter( 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 ); + } + } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java index dc6086c2e..19e2bf97e 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/Utils.java @@ -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; + } + } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommsAddrRec.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommsAddrRec.java index 57ddd0621..54fc9171a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommsAddrRec.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/CommsAddrRec.java @@ -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 ); - } - } } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java index ebea26bf0..0c6f5efa1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/JNIThread.java @@ -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 );