implement util_getUserString and features like formatRemainingTiles

that display a bunch of text.  Started working on util_userQuery.
This commit is contained in:
ehouse 2010-01-16 14:16:27 +00:00
parent a7d4d20998
commit 7c4198c05e
9 changed files with 414 additions and 50 deletions

View file

@ -1,34 +1,33 @@
/* stub this out for now. Need a localization strategy that uses
string.xml */
/* Keep these in sync with the constants in XW_UtilCtxt.java */
#ifndef _LOCALIZEDSTRINCLUDES_H_
#define _LOCALIZEDSTRINCLUDES_H_
# define STRD_ROBOT_TRADED 1000
# define STR_ROBOT_MOVED 1001
# define STRS_VALUES_HEADER 1002
# define STRD_REMAINING_TILES_ADD 1003
# define STRD_UNUSED_TILES_SUB 1004
# define STR_REMOTE_MOVED 1005
# define STRD_TIME_PENALTY_SUB 1006
# define STR_PASS 1000
# define STRS_MOVE_ACROSS 1000
# define STRS_MOVE_DOWN 1000
# define STRS_TRAY_AT_START 1000
# define STRSS_TRADED_FOR 1000
# define STR_PHONY_REJECTED 1000
# define STRD_CUMULATIVE_SCORE 1000
# define STRS_NEW_TILES 1000
# define STR_PASSED 1000
# define STRSD_SUMMARYSCORED 1000
# define STRD_TRADED 1000
# define STR_LOSTTURN 1000
# define STR_COMMIT_CONFIRM 1000
# define STR_LOCAL_NAME 1000
# define STR_NONLOCAL_NAME 1000
# define STR_BONUS_ALL 1000
# define STRD_TURN_SCORE 1000
# define STRD_ROBOT_TRADED 1
# define STR_ROBOT_MOVED 2
# define STRS_VALUES_HEADER 3
# define STRD_REMAINING_TILES_ADD 4
# define STRD_UNUSED_TILES_SUB 5
# define STR_REMOTE_MOVED 6
# define STRD_TIME_PENALTY_SUB 7
# define STR_PASS 8
# define STRS_MOVE_ACROSS 9
# define STRS_MOVE_DOWN 10
# define STRS_TRAY_AT_START 11
# define STRSS_TRADED_FOR 12
# define STR_PHONY_REJECTED 13
# define STRD_CUMULATIVE_SCORE 14
# define STRS_NEW_TILES 15
# define STR_PASSED 16
# define STRSD_SUMMARYSCORED 17
# define STRD_TRADED 18
# define STR_LOSTTURN 19
# define STR_COMMIT_CONFIRM 20
# define STR_LOCAL_NAME 21
# define STR_NONLOCAL_NAME 22
# define STR_BONUS_ALL 23
# define STRD_TURN_SCORE 24
# define N_AND_USER_STRINGS 24
#endif

View file

@ -47,6 +47,8 @@
<string name="button_ok">OK</string>
<string name="button_cancel">Cancel</string>
<string name="button_yes">Yes</string>
<string name="button_no">No</string>
<string name="player_label">Name:</string>
<string name="game_config_open">Open</string>
@ -54,8 +56,14 @@
<string name="error_title">Error</string>
<string name="error_message">Error loading game</string>
<string name="query_title">A question...</string>
<string name="query_trade">Are you sure you want to trade the selected tiles?</string>
<string name="title_tile_picker">Pick tile</string>
<string name="tiles_left_title">Remaining tiles</string>
<string name="counts_values_title">Tile Counts and Values</string>
<string name="history_title">Game History</string>
<!-- system menu for main board view -->
<string name="board_menu_done">Turn done</string>
@ -85,4 +93,30 @@
<string name="gamel_menu_delete_all">Delete all</string>
<string name="gamel_menu_view_hidden">Hidden games</string>
<!-- returned by util_getUserString -->
<string name="strd_robot_traded">Robot traded tiles %d this turn.</string>
<string name="str_robot_moved">The robot made this move:</string>
<string name="strs_values_header">%s counts/values:</string>
<string name="strd_remaining_tiles_add">+ %d [all remaining tiles]</string>
<string name="strd_unused_tiles_sub">- %d [unused tiles]</string>
<string name="str_remote_moved">Remote player made this move:</string>
<string name="strd_time_penalty_sub"></string>
<string name="str_pass"> - %d [time]</string>
<string name="strs_move_across">move (from %s across)</string>
<string name="strs_move_down">move (from %s down)</string>
<string name="strs_tray_at_start">Tray at start: %s</string>
<string name="strss_traded_for">Traded %s for %s.</string>
<string name="str_phony_rejected">Illegal word in move; turn lost!</string>
<string name="strd_cumulative_score">Cumulative score: %d</string>
<string name="strs_new_tiles">New tiles: %s</string>
<string name="str_passed">Passed</string>
<string name="strsd_summaryscored"></string>
<string name="strd_traded">%s:%d</string>
<string name="str_lostturn">Lost turn</string>
<string name="str_commit_confirm">Commit the current move?</string>
<string name="str_local_name">%s</string>
<string name="str_nonlocal_name">%s (remote)</string>
<string name="str_bonus_all">Bonus for using all tiles: 50</string>
<string name="strd_turn_score">Score for turn: %d</string>
</resources>

View file

@ -18,6 +18,9 @@ import android.content.res.Configuration;
import android.content.Intent;
import java.util.concurrent.Semaphore;
import android.net.Uri;
import android.app.Dialog;
import android.app.AlertDialog;
import android.content.DialogInterface;
import org.eehouse.android.xw4.jni.*;
@ -33,6 +36,12 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
private TimerRunnable[] m_timers;
private String m_path;
private final int DLG_OKONLY = 1;
private final int DLG_QUERY = 2;
private String m_dlgBytes = null;
private int m_dlgTitle;
private boolean m_dlgResult;
// call startActivityForResult synchronously
private Semaphore m_forResultWait = new Semaphore(0);
private int m_resultCode = 0;
@ -55,8 +64,41 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
m_jniThread.handle( JNIThread.JNICmd.CMD_TIMER_FIRED,
new Object[] { m_why, m_when, m_handle } );
}
}
@Override
protected Dialog onCreateDialog( int id )
{
Dialog dialog = null;
switch ( id ) {
case DLG_OKONLY:
dialog = new AlertDialog.Builder( BoardActivity.this )
//.setIcon( R.drawable.alert_dialog_icon )
.setTitle( m_dlgTitle )
.setMessage( m_dlgBytes )
.setPositiveButton( R.string.button_ok,
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
Utils.logf( "Ok clicked" );
}
})
.create();
break;
}
return dialog;
} // onCreateDialog
@Override
protected void onPrepareDialog( int id, Dialog dialog )
{
Utils.logf( "onPrepareDialog(id=" + id + ")" );
dialog.setTitle( m_dlgTitle );
((AlertDialog)dialog).setMessage( m_dlgBytes );
super.onPrepareDialog( id, dialog );
}
@Override
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
@ -156,45 +198,63 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
return true;
}
public boolean onOptionsItemSelected(MenuItem item) {
public boolean onOptionsItemSelected( MenuItem item )
{
boolean handled = true;
JNIThread.JNICmd cmd = JNIThread.JNICmd.CMD_NONE;
switch (item.getItemId()) {
case R.id.board_menu_done:
m_jniThread.handle( JNIThread.JNICmd.CMD_COMMIT );
cmd = JNIThread.JNICmd.CMD_COMMIT;
break;
case R.id.board_menu_juggle:
m_jniThread.handle( JNIThread.JNICmd.CMD_JUGGLE );
cmd = JNIThread.JNICmd.CMD_JUGGLE;
break;
case R.id.board_menu_flip:
m_jniThread.handle( JNIThread.JNICmd.CMD_FLIP );
cmd = JNIThread.JNICmd.CMD_FLIP;
break;
case R.id.board_menu_trade:
m_jniThread.handle( JNIThread.JNICmd.CMD_TOGGLE_TRADE );
cmd = JNIThread.JNICmd.CMD_TOGGLE_TRADE;
break;
case R.id.board_menu_tray:
m_jniThread.handle( JNIThread.JNICmd.CMD_TOGGLE_TRAY );
cmd = JNIThread.JNICmd.CMD_TOGGLE_TRAY;
break;
case R.id.board_menu_undo_current:
m_jniThread.handle( JNIThread.JNICmd.CMD_UNDO_CUR );
cmd = JNIThread.JNICmd.CMD_UNDO_CUR;
break;
case R.id.board_menu_undo_last:
m_jniThread.handle( JNIThread.JNICmd.CMD_UNDO_LAST );
cmd = JNIThread.JNICmd.CMD_UNDO_LAST;
break;
case R.id.board_menu_hint:
m_jniThread.handle( JNIThread.JNICmd.CMD_HINT );
cmd = JNIThread.JNICmd.CMD_HINT;
break;
case R.id.board_menu_hint_next:
m_jniThread.handle( JNIThread.JNICmd.CMD_NEXT_HINT );
cmd = JNIThread.JNICmd.CMD_NEXT_HINT;
break;
case R.id.board_menu_values:
m_jniThread.handle( JNIThread.JNICmd.CMD_VALUES );
cmd = JNIThread.JNICmd.CMD_VALUES;
break;
case R.id.board_menu_game_counts:
handled = true;
m_dlgBytes = XwJNI.server_formatDictCounts( m_jniGamePtr, 3 );
m_dlgTitle = R.string.counts_values_title;
showDialog( DLG_OKONLY );
break;
case R.id.board_menu_game_left:
case R.id.board_menu_game_info:
handled = true;
m_dlgBytes = XwJNI.board_formatRemainingTiles( m_jniGamePtr );
m_dlgTitle = R.string.tiles_left_title;
showDialog( DLG_OKONLY );
break;
case R.id.board_menu_game_history:
boolean gameOver = XwJNI.server_getGameIsOver( m_jniGamePtr );
m_dlgBytes = XwJNI.model_writeGameHistory( m_jniGamePtr, gameOver );
m_dlgTitle = R.string.history_title;
showDialog( DLG_OKONLY );
break;
case R.id.board_menu_game_info:
case R.id.board_menu_game_final:
case R.id.board_menu_game_resend:
case R.id.board_menu_file_prefs:
@ -209,6 +269,10 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
handled = false;
}
if ( handled && cmd != JNIThread.JNICmd.CMD_NONE ) {
m_jniThread.handle( cmd );
}
return handled;
}
@ -353,4 +417,110 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable {
{
return !m_jniThread.busy();
}
public String getUserString( int stringCode )
{
int id = 0;
switch( stringCode ) {
case XW_UtilCtxt.STRD_ROBOT_TRADED:
id = R.string.strd_robot_traded;
break;
case XW_UtilCtxt.STR_ROBOT_MOVED:
id = R.string.str_robot_moved;
break;
case XW_UtilCtxt.STRS_VALUES_HEADER:
id = R.string.strs_values_header;
break;
case XW_UtilCtxt.STRD_REMAINING_TILES_ADD:
id = R.string.strd_remaining_tiles_add;
break;
case XW_UtilCtxt.STRD_UNUSED_TILES_SUB:
id = R.string.strd_unused_tiles_sub;
break;
case XW_UtilCtxt.STR_REMOTE_MOVED:
id = R.string.str_remote_moved;
break;
case XW_UtilCtxt.STRD_TIME_PENALTY_SUB:
id = R.string.strd_time_penalty_sub;
break;
case XW_UtilCtxt.STR_PASS:
id = R.string.str_pass;
break;
case XW_UtilCtxt.STRS_MOVE_ACROSS:
id = R.string.strs_move_across;
break;
case XW_UtilCtxt.STRS_MOVE_DOWN:
id = R.string.strs_move_down;
break;
case XW_UtilCtxt.STRS_TRAY_AT_START:
id = R.string.strs_tray_at_start;
break;
case XW_UtilCtxt.STRSS_TRADED_FOR:
id = R.string.strss_traded_for;
break;
case XW_UtilCtxt.STR_PHONY_REJECTED:
id = R.string.str_phony_rejected;
break;
case XW_UtilCtxt.STRD_CUMULATIVE_SCORE:
id = R.string.strd_cumulative_score;
break;
case XW_UtilCtxt.STRS_NEW_TILES:
id = R.string.strs_new_tiles;
break;
case XW_UtilCtxt.STR_PASSED:
id = R.string.str_passed;
break;
case XW_UtilCtxt.STRSD_SUMMARYSCORED:
id = R.string.strsd_summaryscored;
break;
case XW_UtilCtxt.STRD_TRADED:
id = R.string.strd_traded;
break;
case XW_UtilCtxt.STR_LOSTTURN:
id = R.string.str_lostturn;
break;
case XW_UtilCtxt.STR_COMMIT_CONFIRM:
id = R.string.str_commit_confirm;
break;
case XW_UtilCtxt.STR_LOCAL_NAME:
id = R.string.str_local_name;
break;
case XW_UtilCtxt.STR_NONLOCAL_NAME:
id = R.string.str_nonlocal_name;
break;
case XW_UtilCtxt.STR_BONUS_ALL:
id = R.string.str_bonus_all;
break;
case XW_UtilCtxt.STRD_TURN_SCORE:
id = R.string.strd_turn_score;
break;
default:
Utils.logf( "no such stringCode: " + stringCode );
}
String result;
if ( 0 == id ) {
result = "";
} else {
result = getString( id );
}
return result;
}
public boolean userQuery( int id, String query )
{
switch( id ) {
case XW_UtilCtxt.QUERY_COMMIT_TRADE:
query = getString( R.string.query_trade );
break;
case XW_UtilCtxt.QUERY_COMMIT_TURN:
case XW_UtilCtxt.QUERY_ROBOT_MOVE:
case XW_UtilCtxt.QUERY_ROBOT_TRADE:
break;
}
// Need to figure out how this thing can block.
return true;
}
} // class BoardActivity

View file

@ -17,6 +17,40 @@ public interface XW_UtilCtxt {
void clearTimer( int why );
void requestTime();
void remSelected();
static final int STRD_ROBOT_TRADED = 1;
static final int STR_ROBOT_MOVED = 2;
static final int STRS_VALUES_HEADER = 3;
static final int STRD_REMAINING_TILES_ADD = 4;
static final int STRD_UNUSED_TILES_SUB = 5;
static final int STR_REMOTE_MOVED = 6;
static final int STRD_TIME_PENALTY_SUB = 7;
static final int STR_PASS = 8;
static final int STRS_MOVE_ACROSS = 9;
static final int STRS_MOVE_DOWN = 10;
static final int STRS_TRAY_AT_START = 11;
static final int STRSS_TRADED_FOR = 12;
static final int STR_PHONY_REJECTED = 13;
static final int STRD_CUMULATIVE_SCORE = 14;
static final int STRS_NEW_TILES = 15;
static final int STR_PASSED = 16;
static final int STRSD_SUMMARYSCORED = 17;
static final int STRD_TRADED = 18;
static final int STR_LOSTTURN = 19;
static final int STR_COMMIT_CONFIRM = 20;
static final int STR_LOCAL_NAME = 21;
static final int STR_NONLOCAL_NAME = 22;
static final int STR_BONUS_ALL = 23;
static final int STRD_TURN_SCORE = 24;
String getUserString( int stringCode );
static final int QUERY_COMMIT_TURN = 0;
static final int QUERY_COMMIT_TRADE = 1;
static final int QUERY_ROBOT_MOVE = 2;
static final int QUERY_ROBOT_TRADE = 3;
boolean userQuery( int id, String query );
// Don't need this unless we have a scroll thumb to indicate position
//void yOffsetChange( int oldOffset, int newOffset );

View file

@ -77,6 +77,12 @@ public class XwJNI {
boolean[] workRemains );
public static native boolean board_beginTrade( int gamePtr );
public static native String board_formatRemainingTiles( int gamePtr );
public static native void server_handleUndo( int gamePtr );
public static native boolean server_do( int gamePtr );
public static native String server_formatDictCounts( int gamePtr, int nCols );
public static native boolean server_getGameIsOver( int gamePtr );
public static native String model_writeGameHistory( int gamePtr, boolean gameOver );
}

View file

@ -3,6 +3,7 @@
#include "andutils.h"
#include "comtypes.h"
#include "xwstream.h"
void
and_assert( const char* test, int line, const char* file, const char* func )
@ -102,13 +103,15 @@ setString( JNIEnv* env, jobject obj, const char* name, const XP_UCHAR* value )
bool success = false;
jclass cls = (*env)->GetObjectClass( env, obj );
jfieldID fid = (*env)->GetFieldID( env, cls, name, "Ljava/lang/String;" );
(*env)->DeleteLocalRef( env, cls );
if ( 0 != fid ) {
jstring str = (*env)->NewStringUTF( env, value );
(*env)->SetObjectField( env, obj, fid, str );
success = true;
(*env)->DeleteLocalRef( env, str );
}
(*env)->DeleteLocalRef( env, cls );
return success;
}
@ -213,6 +216,20 @@ makeStringArray( JNIEnv *env, int siz, const XP_UCHAR** vals )
return jarray;
}
jstring
streamToJString( MPFORMAL JNIEnv *env, XWStreamCtxt* stream )
{
int len = stream_getSize( stream );
XP_UCHAR* buf = XP_MALLOC( mpool, 1 + len );
stream_getBytes( stream, buf, len );
buf[len] = '\0';
jstring jstr = (*env)->NewStringUTF( env, buf );
XP_FREE( mpool, buf );
return jstr;
}
jmethodID
getMethodID( JNIEnv* env, jobject obj, const char* proc, const char* sig )
{

View file

@ -5,7 +5,8 @@
#include <jni.h>
#include "xptypes.h"
#include "comtypes.h"
#include "mempool.h"
XP_U32 and_ntohl(XP_U32 l);
XP_U16 and_ntohs(XP_U16 l);
@ -26,6 +27,7 @@ jintArray makeIntArray( JNIEnv *env, int size, const jint* vals );
int getIntFromArray( JNIEnv* env, jintArray arr, bool del );
jobjectArray makeStringArray( JNIEnv *env, int size, const XP_UCHAR** vals );
jstring streamToJString( MPFORMAL JNIEnv* env, XWStreamCtxt* stream );
/* Note: jmethodID can be cached. Should not look up more than once. */
jmethodID getMethodID( JNIEnv* env, jobject obj, const char* proc,

View file

@ -23,6 +23,7 @@
#include "utilwrapper.h"
#include "andutils.h"
#include "LocalizedStrIncludes.h"
typedef struct _TimerStorage {
XWTimerProc proc;
@ -34,6 +35,7 @@ typedef struct _AndUtil {
JNIEnv** env;
jobject j_util; /* global ref to object implementing XW_UtilCtxt */
TimerStorage timerStorage[NUM_TIMERS_PLUS_ONE];
XP_UCHAR* userStrings[N_AND_USER_STRINGS];
} AndUtil;
@ -76,7 +78,19 @@ and_util_userError( XW_UtilCtxt* uc, UtilErrID id )
static XP_Bool
and_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
{
XP_LOGF( "%s(id=%d)", __func__, id );
XP_ASSERT( id < QUERY_LAST_COMMON );
UTIL_CBK_HEADER("userQuery", "(ILjava/lang/String;)Z" );
jstring jstr = NULL;
if ( NULL != stream ) {
jstr = streamToJString( MPPARM(util->util.mpool) env, stream );
}
jboolean result = (*env)->CallBooleanMethod( env, util->j_util, mid,
id, jstr );
if ( NULL != jstr ) {
(*env)->DeleteLocalRef( env, jstr );
}
return result;
}
static XP_S16
@ -225,8 +239,27 @@ and_util_makeEmptyDict( XW_UtilCtxt* uc )
static const XP_UCHAR*
and_util_getUserString( XW_UtilCtxt* uc, XP_U16 stringCode )
{
LOG_FUNC();
return "";
UTIL_CBK_HEADER("getUserString", "(I)Ljava/lang/String;" );
int index = stringCode - 1; /* see LocalizedStrIncludes.h */
XP_ASSERT( index < VSIZE( util->userStrings ) );
if ( ! util->userStrings[index] ) {
jstring jresult = (*env)->CallObjectMethod( env, util->j_util, mid,
stringCode );
jsize len = (*env)->GetStringUTFLength( env, jresult );
XP_UCHAR* buf = XP_MALLOC( util->util.mpool, len + 1 );
const char* jchars = (*env)->GetStringUTFChars( env, jresult, NULL );
XP_MEMCPY( buf, jchars, len );
buf[len] = '\0';
XP_LOGF( "got string from java: %s", buf );
(*env)->ReleaseStringUTFChars( env, jresult, jchars );
(*env)->DeleteLocalRef( env, jresult );
util->userStrings[index] = buf;
}
LOG_RETURNF( "%s", util->userStrings[index] );
return util->userStrings[index];
}
@ -342,6 +375,12 @@ destroyUtil( XW_UtilCtxt* utilc )
{
AndUtil* util = (AndUtil*)utilc;
JNIEnv *env = *util->env;
int ii;
for ( ii = 0; ii < VSIZE(util->userStrings); ++ii ) {
XP_FREE( util->util.mpool, util->userStrings[ii] );
}
(*env)->DeleteGlobalRef( env, util->j_util );
XP_FREE( util->util.mpool, util->util.vtable );
XP_FREE( util->util.mpool, util );

View file

@ -14,9 +14,7 @@
#include "utilwrapper.h"
#include "drawwrapper.h"
#include "anddict.h"
/* static JNIEnv* g_env = NULL; */
#include "andutils.h"
static CurGameInfo*
makeGI( MPFORMAL JNIEnv* env, jobject j_gi )
@ -59,9 +57,9 @@ makeGI( MPFORMAL JNIEnv* env, jobject j_gi )
getBool( env, jlp, "isRobot", &lp->isRobot );
getBool( env, jlp, "isLocal", &lp->isLocal );
getString( env, jlp, "name", &buf, VSIZE(buf) );
getString( env, jlp, "name", buf, VSIZE(buf) );
lp->name = copyString( mpool, buf );
getString( env, jlp, "password", &buf, VSIZE(buf) );
getString( env, jlp, "password", buf, VSIZE(buf) );
lp->password = copyString( mpool, buf );
lp->secondsUsed = 0;
@ -132,6 +130,14 @@ loadCommonPrefs( JNIEnv* env, CommonPrefs* cp, jobject j_cp )
return success;
}
static XWStreamCtxt*
and_empty_stream( MPFORMAL AndGlobals* globals )
{
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr,
NULL, 0, NULL );
return stream;
}
/****************************************************
* These two methods are stateless: no gamePtr
****************************************************/
@ -630,3 +636,60 @@ Java_org_eehouse_android_xw4_jni_XwJNI_timerFired
XWJNI_END();
return result;
}
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1formatRemainingTiles
(JNIEnv* env, jclass C, jint gamePtr )
{
jstring result;
XWJNI_START();
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globals->vtMgr,
NULL, 0, NULL );
board_formatRemainingTiles( state->game.board, stream );
result = streamToJString( MPPARM(mpool) env, stream );
stream_destroy( stream );
(*env)->DeleteLocalRef( env, result );
XWJNI_END();
return result;
}
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_server_1formatDictCounts
( JNIEnv* env, jclass C, jint gamePtr, jint nCols )
{
jstring result;
XWJNI_START();
XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals );
server_formatDictCounts( state->game.server, stream, nCols );
result = streamToJString( MPPARM(mpool) env, stream );
stream_destroy( stream );
(*env)->DeleteLocalRef( env, result );
XWJNI_END();
return result;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_server_1getGameIsOver
( JNIEnv* env, jclass C, jint gamePtr )
{
jboolean result;
XWJNI_START();
result = server_getGameIsOver( state->game.server );
XWJNI_END();
return result;
}
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_model_1writeGameHistory
( JNIEnv* env, jclass C, jint gamePtr, jboolean gameOver )
{
jstring result;
XWJNI_START();
XWStreamCtxt* stream = and_empty_stream( MPPARM(mpool) globals );
model_writeGameHistory( state->game.model, stream, state->game.server,
gameOver );
result = streamToJString( MPPARM(mpool) env, stream );
XWJNI_END();
return result;
}