mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-30 10:26:58 +01:00
When user dismisses GameConfig form, check if the game being viewed
has any state (moves) and if the changes being made would invalidate that state and force a reset. If so, give the user a choice between save and discard. Means CurGameInfo behaves slightly differently if a game is in progress. Also fix game reset to preserve comms address.
This commit is contained in:
parent
11d6f7417e
commit
2761772e05
6 changed files with 229 additions and 50 deletions
|
@ -50,6 +50,8 @@
|
|||
<string name="button_cancel">Cancel</string>
|
||||
<string name="button_yes">Yes</string>
|
||||
<string name="button_no">No</string>
|
||||
<string name="button_save">Save</string>
|
||||
<string name="button_discard">Discard</string>
|
||||
|
||||
<string name="player_label">Name:</string>
|
||||
<string name="game_config_open">Open</string>
|
||||
|
@ -161,7 +163,13 @@
|
|||
<string name="download_dicts">Download more...</string>
|
||||
|
||||
<string name="dict_url">http://eehouse.org/and_dicts</string>
|
||||
<!--string name="dict_url">http://10.0.2.2/~eehouse/and_dicts</string-->
|
||||
|
||||
<string name="confirm_save_title">Confirm save</string>
|
||||
<string name="confirm_save">This game is in play. If you save
|
||||
these changes it must be restarted. Do you want to save or
|
||||
discard these changes?</string>
|
||||
|
||||
<string name="hints_allowed">Allow hints</string>
|
||||
<string name="use_timer">Enable timer</string>
|
||||
<string name="color_tiles">Color tiles</string>
|
||||
|
|
|
@ -41,6 +41,7 @@ import android.widget.CompoundButton;
|
|||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.KeyEvent;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
|
@ -59,11 +60,13 @@ public class GameConfig extends Activity implements View.OnClickListener {
|
|||
private static final int ROLE_EDIT_SMS = 3;
|
||||
private static final int ROLE_EDIT_BT = 4;
|
||||
private static final int FORCE_REMOTE = 5;
|
||||
private static final int CONFIRM_CHANGE = 6;
|
||||
|
||||
private Button m_addPlayerButton;
|
||||
private Button m_configureButton;
|
||||
private String m_path;
|
||||
private CurGameInfo m_gi;
|
||||
private CurGameInfo m_giOrig;
|
||||
private int m_whichPlayer;
|
||||
private Dialog m_curDialog;
|
||||
private Spinner m_roleSpinner;
|
||||
|
@ -73,10 +76,12 @@ public class GameConfig extends Activity implements View.OnClickListener {
|
|||
private String[] m_dicts;
|
||||
private int m_browsePosition;
|
||||
private LinearLayout m_playerLayout;
|
||||
private CommsAddrRec m_carOrig;
|
||||
private CommsAddrRec m_car;
|
||||
private CommonPrefs m_cp;
|
||||
private boolean m_canDoSMS = false;
|
||||
private boolean m_canDoBT = false;
|
||||
private int m_nMoves = 0;
|
||||
private CommsAddrRec.CommsConnType[] m_types;
|
||||
|
||||
class RemoteChoices implements ListAdapter {
|
||||
|
@ -174,6 +179,27 @@ public class GameConfig extends Activity implements View.OnClickListener {
|
|||
}
|
||||
});
|
||||
break;
|
||||
case CONFIRM_CHANGE:
|
||||
dialog = new AlertDialog.Builder( this )
|
||||
.setTitle( R.string.confirm_save_title )
|
||||
.setMessage( R.string.confirm_save )
|
||||
.setPositiveButton( R.string.button_save,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick( DialogInterface dlg,
|
||||
int whichButton ) {
|
||||
applyChanges( true );
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.setNegativeButton( R.string.button_discard,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick( DialogInterface dlg,
|
||||
int whichButton ) {
|
||||
finish();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
break;
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
@ -312,28 +338,25 @@ public class GameConfig extends Activity implements View.OnClickListener {
|
|||
m_path = m_path.substring( 1 );
|
||||
}
|
||||
|
||||
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, JNIUtilsImpl.get(),
|
||||
m_gi, dictBytes, m_cp ) ) {
|
||||
XwJNI.game_makeNewGame( gamePtr, m_gi, JNIUtilsImpl.get(),
|
||||
m_cp, dictBytes );
|
||||
}
|
||||
m_giOrig = new CurGameInfo( this );
|
||||
Utils.loadMakeGame( this, gamePtr, m_giOrig, m_path );
|
||||
m_nMoves = XwJNI.model_getNMoves( gamePtr );
|
||||
m_giOrig.setInProgress( 0 < m_nMoves );
|
||||
m_gi = new CurGameInfo( m_giOrig );
|
||||
|
||||
int curSel = listAvailableDicts( m_gi.dictName );
|
||||
|
||||
m_car = new CommsAddrRec();
|
||||
m_carOrig = new CommsAddrRec();
|
||||
if ( XwJNI.game_hasComms( gamePtr ) ) {
|
||||
XwJNI.comms_getAddr( gamePtr, m_car );
|
||||
XwJNI.comms_getAddr( gamePtr, m_carOrig );
|
||||
} else {
|
||||
XwJNI.comms_getInitialAddr( m_car );
|
||||
XwJNI.comms_getInitialAddr( m_carOrig );
|
||||
}
|
||||
XwJNI.game_dispose( gamePtr );
|
||||
|
||||
m_car = new CommsAddrRec( m_carOrig );
|
||||
|
||||
setContentView(R.layout.game_config);
|
||||
|
||||
m_addPlayerButton = (Button)findViewById(R.id.add_player);
|
||||
|
@ -439,13 +462,6 @@ public class GameConfig extends Activity implements View.OnClickListener {
|
|||
adjustVisibility();
|
||||
} // onCreate
|
||||
|
||||
@Override
|
||||
protected void onPause()
|
||||
{
|
||||
saveChanges();
|
||||
super.onPause(); // skip this and get a crash :-)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateContextMenu( ContextMenu menu, View view,
|
||||
ContextMenuInfo menuInfo ) {
|
||||
|
@ -537,6 +553,27 @@ public class GameConfig extends Activity implements View.OnClickListener {
|
|||
}
|
||||
} // onClick
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown( int keyCode, KeyEvent event )
|
||||
{
|
||||
boolean consumed = false;
|
||||
if ( keyCode == KeyEvent.KEYCODE_BACK ) {
|
||||
saveChanges();
|
||||
if ( 0 < m_nMoves && (m_giOrig.changesMatter(m_gi)
|
||||
|| m_carOrig.changesMatter(m_car) ) ) {
|
||||
showDialog( CONFIRM_CHANGE );
|
||||
consumed = true;
|
||||
} else {
|
||||
applyChanges( false );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !consumed ) {
|
||||
consumed = super.onKeyDown( keyCode, event );
|
||||
}
|
||||
return consumed;
|
||||
}
|
||||
|
||||
private void loadPlayers()
|
||||
{
|
||||
m_playerLayout.removeAllViews();
|
||||
|
@ -715,17 +752,39 @@ public class GameConfig extends Activity implements View.OnClickListener {
|
|||
|
||||
position = m_connectSpinner.getSelectedItemPosition();
|
||||
m_car.conType = m_types[ position ];
|
||||
}
|
||||
|
||||
private void applyChanges( boolean forceNew )
|
||||
{
|
||||
// This should be a separate function, commitChanges() or
|
||||
// somesuch. But: do we have a way to save changes to a gi
|
||||
// that don't reset the game, e.g. player name for standalone
|
||||
// games?
|
||||
byte[] dictBytes = Utils.openDict( this, m_gi.dictName );
|
||||
int gamePtr = XwJNI.initJNI();
|
||||
XwJNI.game_makeNewGame( gamePtr, m_gi, JNIUtilsImpl.get(),
|
||||
m_cp, dictBytes );
|
||||
boolean madeGame = false;
|
||||
|
||||
if ( !forceNew ) {
|
||||
byte[] stream = Utils.savedGame( this, m_path );
|
||||
// Will fail if there's nothing in the stream but a gi.
|
||||
madeGame = XwJNI.game_makeFromStream( gamePtr, stream,
|
||||
JNIUtilsImpl.get(),
|
||||
new CurGameInfo(this),
|
||||
dictBytes, m_cp );
|
||||
}
|
||||
|
||||
if ( forceNew || !madeGame ) {
|
||||
m_gi.setInProgress( false );
|
||||
m_gi.fixup();
|
||||
XwJNI.game_makeNewGame( gamePtr, m_gi, JNIUtilsImpl.get(),
|
||||
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 );
|
||||
|
||||
Utils.saveGame( this, gamePtr, m_gi, m_path );
|
||||
XwJNI.game_dispose( gamePtr );
|
||||
}
|
||||
|
||||
|
|
|
@ -124,18 +124,12 @@ public class GamesList extends ListActivity implements View.OnClickListener {
|
|||
break;
|
||||
|
||||
case R.id.list_item_reset:
|
||||
case R.id.list_item_new_from:
|
||||
stream = Utils.savedGame( this, path );
|
||||
CurGameInfo gi = new CurGameInfo( this );
|
||||
XwJNI.gi_from_stream( gi, stream );
|
||||
stream = XwJNI.gi_to_stream( gi );
|
||||
if ( R.id.list_item_reset == id ) {
|
||||
Utils.saveGame( this, stream, path );
|
||||
} else {
|
||||
Utils.saveGame( this, stream );
|
||||
}
|
||||
Utils.resetGame( this, path, path );
|
||||
break;
|
||||
|
||||
case R.id.list_item_new_from:
|
||||
Utils.resetGame( this, path );
|
||||
break;
|
||||
|
||||
case R.id.list_item_copy:
|
||||
stream = Utils.savedGame( this, path );
|
||||
Utils.saveGame( this, stream );
|
||||
|
|
|
@ -25,6 +25,8 @@ import java.util.Formatter;
|
|||
import android.view.LayoutInflater;
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eehouse.android.xw4.jni.*;
|
||||
|
||||
public class Utils {
|
||||
static final String TAG = "EJAVA";
|
||||
|
||||
|
@ -97,6 +99,73 @@ public class Utils {
|
|||
return stream;
|
||||
} // savedGame
|
||||
|
||||
/**
|
||||
* Open an existing game, and use its gi and comms addr as the
|
||||
* basis for a new one.
|
||||
*/
|
||||
public static void resetGame( Context context, String pathIn,
|
||||
String pathOut )
|
||||
{
|
||||
int gamePtr = XwJNI.initJNI();
|
||||
CurGameInfo gi = new CurGameInfo( context );
|
||||
CommsAddrRec addr = null;
|
||||
|
||||
loadMakeGame( context, gamePtr, gi, pathIn );
|
||||
byte[] dictBytes = Utils.openDict( context, gi.dictName );
|
||||
|
||||
if ( XwJNI.game_hasComms( gamePtr ) ) {
|
||||
addr = new CommsAddrRec();
|
||||
XwJNI.comms_getAddr( gamePtr, addr );
|
||||
}
|
||||
XwJNI.game_dispose( gamePtr );
|
||||
|
||||
gi.setInProgress( false );
|
||||
gi.fixup();
|
||||
|
||||
gamePtr = XwJNI.initJNI();
|
||||
XwJNI.game_makeNewGame( gamePtr, gi, JNIUtilsImpl.get(),
|
||||
CommonPrefs.get(), dictBytes );
|
||||
if ( null != addr ) {
|
||||
XwJNI.comms_setAddr( gamePtr, addr );
|
||||
}
|
||||
saveGame( context, gamePtr, gi, pathOut );
|
||||
XwJNI.game_dispose( gamePtr );
|
||||
}
|
||||
|
||||
public static void resetGame( Context context, String pathIn )
|
||||
{
|
||||
resetGame( context, pathIn, newName( context ) );
|
||||
}
|
||||
|
||||
public static void loadMakeGame( Context context, int gamePtr,
|
||||
CurGameInfo gi, String path )
|
||||
{
|
||||
byte[] stream = savedGame( context, path );
|
||||
XwJNI.gi_from_stream( gi, stream );
|
||||
byte[] dictBytes = Utils.openDict( context, gi.dictName );
|
||||
|
||||
boolean madeGame = XwJNI.game_makeFromStream( gamePtr, stream,
|
||||
JNIUtilsImpl.get(),
|
||||
gi, dictBytes,
|
||||
CommonPrefs.get() );
|
||||
if ( !madeGame ) {
|
||||
XwJNI.game_makeNewGame( gamePtr, gi, JNIUtilsImpl.get(),
|
||||
CommonPrefs.get(), dictBytes );
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveGame( Context context, int gamePtr,
|
||||
CurGameInfo gi, String path )
|
||||
{
|
||||
byte[] stream = XwJNI.game_saveToStream( gamePtr, gi );
|
||||
saveGame( context, stream, path );
|
||||
}
|
||||
|
||||
public static void saveGame( Context context, int gamePtr,
|
||||
CurGameInfo gi )
|
||||
{
|
||||
saveGame( context, gamePtr, gi, newName( context ) );
|
||||
}
|
||||
|
||||
public static void saveGame( Context context, byte[] bytes, String path )
|
||||
{
|
||||
|
|
|
@ -38,8 +38,20 @@ public class CommsAddrRec {
|
|||
ip_relay_port = 10999;
|
||||
}
|
||||
|
||||
public CommsAddrRec( CommsAddrRec src ) {
|
||||
this.copyFrom(src );
|
||||
public CommsAddrRec( final CommsAddrRec src )
|
||||
{
|
||||
this.copyFrom( src );
|
||||
}
|
||||
|
||||
public boolean changesMatter( final CommsAddrRec other )
|
||||
{
|
||||
boolean matter = conType != other.conType;
|
||||
if ( !matter ) {
|
||||
matter = ! ip_relay_invite.equals( other.ip_relay_invite )
|
||||
|| ! ip_relay_hostName.equals( other.ip_relay_hostName )
|
||||
|| ip_relay_port != other.ip_relay_port;
|
||||
}
|
||||
return matter;
|
||||
}
|
||||
|
||||
private void copyFrom( CommsAddrRec src )
|
||||
|
@ -49,13 +61,4 @@ public class CommsAddrRec {
|
|||
ip_relay_hostName = src.ip_relay_hostName;
|
||||
ip_relay_port = src.ip_relay_port;
|
||||
}
|
||||
|
||||
private static CommsAddrRec s_car;
|
||||
public static final CommsAddrRec get()
|
||||
{
|
||||
if ( null == s_car ) {
|
||||
s_car = new CommsAddrRec();
|
||||
}
|
||||
return s_car;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,10 @@ public class CurGameInfo {
|
|||
|
||||
private int[] m_visiblePlayers;
|
||||
private int m_nVisiblePlayers;
|
||||
private boolean m_inProgress;
|
||||
|
||||
public CurGameInfo( Context context ) {
|
||||
m_inProgress = false;
|
||||
nPlayers = 2;
|
||||
boardSize = 15;
|
||||
players = new LocalPlayer[MAX_NUM_PLAYERS];
|
||||
|
@ -57,11 +59,13 @@ public class CurGameInfo {
|
|||
players[ii] = new LocalPlayer(ii);
|
||||
}
|
||||
|
||||
m_visiblePlayers = new int[MAX_NUM_PLAYERS];
|
||||
figureVisible();
|
||||
}
|
||||
|
||||
public CurGameInfo( CurGameInfo src ) {
|
||||
public CurGameInfo( CurGameInfo src )
|
||||
{
|
||||
m_inProgress = src.m_inProgress;
|
||||
gameID = src.gameID;
|
||||
nPlayers = src.nPlayers;
|
||||
boardSize = src.boardSize;
|
||||
players = new LocalPlayer[MAX_NUM_PLAYERS];
|
||||
|
@ -94,6 +98,43 @@ public class CurGameInfo {
|
|||
}
|
||||
}
|
||||
|
||||
public void setInProgress( boolean inProgress )
|
||||
{
|
||||
m_inProgress = inProgress;
|
||||
figureVisible();
|
||||
}
|
||||
|
||||
/** return true if any of the changes made would invalide a game
|
||||
* in progress, i.e. require that it be restarted with the new
|
||||
* params. E.g. changing a player to a robot is harmless for a
|
||||
* local-only game but illegal for a connected one.
|
||||
*/
|
||||
public boolean changesMatter( final CurGameInfo other )
|
||||
{
|
||||
boolean matter = nPlayers != other.nPlayers
|
||||
|| serverRole != other.serverRole
|
||||
|| !dictName.equals( other.dictName )
|
||||
|| boardSize != other.boardSize
|
||||
|| hintsNotAllowed != other.hintsNotAllowed
|
||||
|| allowPickTiles != other.allowPickTiles
|
||||
|| phoniesAction != other.phoniesAction;
|
||||
|
||||
if ( !matter && DeviceRole.SERVER_STANDALONE != serverRole ) {
|
||||
for ( int ii = 0; ii < nPlayers; ++ii ) {
|
||||
LocalPlayer me = players[ii];
|
||||
LocalPlayer him = other.players[ii];
|
||||
matter = me.isRobot != him.isRobot
|
||||
|| me.isLocal != him.isLocal
|
||||
|| !me.name.equals( him.name );
|
||||
if ( matter ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matter;
|
||||
}
|
||||
|
||||
public int remoteCount()
|
||||
{
|
||||
figureVisible();
|
||||
|
@ -129,7 +170,7 @@ public class CurGameInfo {
|
|||
nPlayers = m_nVisiblePlayers;
|
||||
}
|
||||
|
||||
if ( serverRole != DeviceRole.SERVER_ISSERVER ) {
|
||||
if ( !m_inProgress && serverRole != DeviceRole.SERVER_ISSERVER ) {
|
||||
for ( int ii = 0; ii < nPlayers; ++ii ) {
|
||||
players[ii].isLocal = true;
|
||||
}
|
||||
|
@ -263,9 +304,14 @@ public class CurGameInfo {
|
|||
|
||||
private void figureVisible()
|
||||
{
|
||||
if ( null == m_visiblePlayers ) {
|
||||
m_visiblePlayers = new int[MAX_NUM_PLAYERS];
|
||||
}
|
||||
|
||||
m_nVisiblePlayers = 0;
|
||||
for ( int ii = 0; ii < nPlayers; ++ii ) {
|
||||
if ( serverRole != DeviceRole.SERVER_ISCLIENT
|
||||
if ( m_inProgress
|
||||
|| serverRole != DeviceRole.SERVER_ISCLIENT
|
||||
|| players[ii].isLocal ) {
|
||||
m_visiblePlayers[m_nVisiblePlayers++] = ii;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue