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 4962279c6..fbdeb2160 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -11,12 +11,16 @@ import android.view.MenuInflater; import android.content.res.AssetManager; import java.io.InputStream; import android.os.Handler; +import java.io.FileOutputStream; +import java.io.FileInputStream; import android.content.res.Configuration; import org.eehouse.android.xw4.jni.*; public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable { + private static final String CUR_GAME = "cur_game"; + private BoardView m_view; private int m_jniGamePtr; private CurGameInfo m_gi; @@ -73,12 +77,26 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable { } // am.close(); don't close! won't work subsequently - Utils.logf( "calling game_makeNewGame; passing bytes: " + dictBytes.length ); - m_jniGamePtr = XwJNI.game_makeNewGame( m_gi, this, m_view, 0, - m_prefs, null, dictBytes ); + m_jniGamePtr = XwJNI.initJNI(); + + byte[] stream = savedGame(); + if ( null == stream || + ! XwJNI.game_makeFromStream( m_jniGamePtr, stream, + m_gi, dictBytes, this, + m_view, m_prefs, + null ) ) { + XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, this, m_view, 0, + m_prefs, null, dictBytes ); + } m_view.startHandling( this, m_jniGamePtr, m_gi ); XwJNI.server_do( m_jniGamePtr ); + } // onCreate + + protected void onPause() { + // save state here + saveGame(); + super.onPause(); } protected void onDestroy() @@ -106,7 +124,6 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable { return draw; } - public boolean onOptionsItemSelected(MenuItem item) { boolean draw = false; boolean handled = true; @@ -152,6 +169,39 @@ public class BoardActivity extends Activity implements XW_UtilCtxt, Runnable { return handled; } + private void saveGame() + { + byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, m_gi ); + + try { + FileOutputStream out = openFileOutput( CUR_GAME, MODE_PRIVATE ); + out.write( state ); + out.close(); + } catch ( java.io.IOException ex ) { + Utils.logf( "got IOException: " + ex.toString() ); + } + } + + private byte[] savedGame() + { + byte[] stream = null; + try { + FileInputStream in = openFileInput( CUR_GAME ); + int len = in.available(); + Utils.logf( "savedGame: got " + len + " bytes." ); + stream = new byte[len]; + in.read( stream, 0, len ); + in.close(); + } catch ( java.io.FileNotFoundException fnf ) { + Utils.logf( fnf.toString() ); + stream = null; + } catch ( java.io.IOException io ) { + Utils.logf( io.toString() ); + stream = null; + } + return stream; + } + // gets called for orientation changes only if // android:configChanges="orientation" set in AndroidManifest.xml diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java index b2b873ac4..f6e6d4bc2 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/XwJNI.java @@ -19,9 +19,25 @@ public class XwJNI { int when, int handle ); // Game methods - public static native int game_makeNewGame( CurGameInfo gi, XW_UtilCtxt util, - DrawCtx draw, int gameID, CommonPrefs cp, - TransportProcs procs, byte[] dict ); + public static native int initJNI(); + public static native void game_makeNewGame( int gamePtr, + CurGameInfo gi, + XW_UtilCtxt util, + DrawCtx draw, int gameID, + CommonPrefs cp, + TransportProcs procs, + byte[] dict ); + public static native boolean game_makeFromStream( int gamePtr, + byte[] stream, + CurGameInfo gi, + byte[] dict, + XW_UtilCtxt util, + DrawCtx draw, + CommonPrefs cp, + TransportProcs procs ); + + public static native byte[] game_saveToStream( int gamePtr, + CurGameInfo gi ); public static native void game_dispose( int gamePtr ); // Board methods diff --git a/xwords4/android/xwjni.c b/xwords4/android/xwjni.c index 42e2f76a5..0547c2a46 100644 --- a/xwords4/android/xwjni.c +++ b/xwords4/android/xwjni.c @@ -21,7 +21,6 @@ makeGI( MPFORMAL JNIEnv* env, jobject j_gi ) { CurGameInfo* gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*gi) ); int nPlayers, robotSmartness, boardSize; - int ii; XP_UCHAR buf[256]; /* in case needs whole path */ bool success = getInt( env, j_gi, "nPlayers", &nPlayers ) @@ -49,6 +48,7 @@ makeGI( MPFORMAL JNIEnv* env, jobject j_gi ) if ( getObject( env, j_gi, "players", "[Lorg/eehouse/android/xw4/jni/LocalPlayer;", &jplayers ) ) { + int ii; for ( ii = 0; ii < gi->nPlayers; ++ii ) { LocalPlayer* lp = &gi->players[ii]; @@ -74,6 +74,45 @@ makeGI( MPFORMAL JNIEnv* env, jobject j_gi ) return gi; } /* makeGI */ +static void +setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi ) +{ + // set fields + bool success = setInt( env, jgi, "nPlayers", gi->nPlayers ) + && setInt( env, jgi, "boardSize", gi->boardSize ) + && setInt( env, jgi, "robotSmartness", gi->robotSmartness ) + && setBool( env, jgi, "hintsNotAllowed", gi->hintsNotAllowed ) + && setBool( env, jgi, "timerEnabled", gi->timerEnabled ) + && setBool( env, jgi, "allowPickTiles", gi->allowPickTiles ) + && setBool( env, jgi, "allowHintRect", gi->allowHintRect ) + && setString( env, jgi, "dictName", gi->dictName ) + ; + XP_ASSERT( success ); + + jobject jplayers; + if ( getObject( env, jgi, "players", + "[Lorg/eehouse/android/xw4/jni/LocalPlayer;", + &jplayers ) ) { + int ii; + for ( ii = 0; ii < gi->nPlayers; ++ii ) { + const LocalPlayer* lp = &gi->players[ii]; + + jobject jlp = (*env)->GetObjectArrayElement( env, jplayers, ii ); + + setBool( env, jlp, "isRobot", lp->isRobot ); + setBool( env, jlp, "isLocal", lp->isLocal ); + setString( env, jlp, "name", lp->name ); + setString( env, jlp, "password", lp->password ); + setInt( env, jlp, "secondsUsed", lp->secondsUsed ); + + (*env)->DeleteLocalRef( env, jlp ); + } + (*env)->DeleteLocalRef( env, jplayers ); + } else { + XP_ASSERT(0); + } +} + static void destroyGI( MPFORMAL CurGameInfo* gi ) { @@ -92,11 +131,6 @@ loadCommonPrefs( JNIEnv* env, CommonPrefs* cp, jobject j_cp ) return success; } -/* - generated by running: - (cd /home/andy/dev/XWORDS/android/XWords4/bin/classes; javah -o /tmp/foo.h org.eehouse.android.xw4.jni.XwJNI) -*/ - typedef struct _GameAndMPool { XWGame game; AndGlobals* globals; @@ -104,19 +138,30 @@ typedef struct _GameAndMPool { } GameAndMPool; JNIEXPORT jint JNICALL -Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame -( JNIEnv *env, jclass C, jobject j_gi, jobject j_util, jobject j_draw, - jint gameID, jobject j_cp, jobject j_procs, jbyteArray jDictBytes ) +Java_org_eehouse_android_xw4_jni_XwJNI_initJNI +( JNIEnv* env, jclass C ) { - LOG_FUNC(); MemPoolCtx* mpool = mpool_make(); - - GameAndMPool* game = XP_MALLOC( mpool, sizeof(*game) ); + GameAndMPool* game = (GameAndMPool*)XP_CALLOC( mpool, sizeof(*game) ); AndGlobals* globals = XP_MALLOC( mpool, sizeof( *globals ) ); game->mpool = mpool; game->globals = globals; globals->vtMgr = make_vtablemgr(MPPARM_NOCOMMA(mpool)); + return (jint) game; +} + +JNIEXPORT void JNICALL +Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame +( JNIEnv* env, jclass C, jint gamePtr, jobject j_gi, jobject j_util, + jobject j_draw, jint gameID, jobject j_cp, jobject j_procs, + jbyteArray jDictBytes ) +{ + LOG_FUNC(); + GameAndMPool* game = (GameAndMPool*)gamePtr; + MemPoolCtx* mpool = game->mpool; + AndGlobals* globals = game->globals; + CurGameInfo* gi = makeGI( MPPARM(mpool) env, j_gi ); globals->gi = gi; XW_UtilCtxt* util = makeUtil( MPPARM(mpool) env, j_util, gi, globals ); @@ -142,13 +187,70 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeNewGame } #endif XP_ASSERT( !!dict ); - - globals->dict = dict; model_setDictionary( game->game.model, dict ); - - return (jint) game; } /* makeNewGame */ +JNIEXPORT jboolean JNICALL +Java_org_eehouse_android_xw4_jni_XwJNI_game_1makeFromStream +( JNIEnv* env, jclass C, jint gamePtr, jbyteArray jstream, + jobject /*out*/jgi, jbyteArray jdict, jobject jutil, jobject jdraw, + jobject jcp, jobject jprocs ) +{ + GameAndMPool* game = (GameAndMPool*)gamePtr; + MemPoolCtx* mpool = game->mpool; + AndGlobals* globals = game->globals; + + globals->gi = (CurGameInfo*)XP_CALLOC( mpool, sizeof(*globals->gi) ); + DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jdict ); + globals->util = makeUtil( MPPARM(mpool) env, jutil, globals->gi, globals ); + globals->dctx = makeDraw( MPPARM(mpool) env, jdraw ); + + jbyte* jelems = (*env)->GetByteArrayElements( env, jstream, NULL ); + XWStreamCtxt* stream = mem_stream_make( game->mpool, globals->vtMgr, + NULL, 0, NULL ); + int len = (*env)->GetArrayLength( env, jstream ); + XP_LOGF( "putting %d bytes into stream", len ); + stream_putBytes( stream, jelems, len ); + (*env)->ReleaseByteArrayElements( env, jstream, jelems, 0 ); + + CommonPrefs cp; + (void)loadCommonPrefs( env, &cp, jcp ); + XP_Bool result = game_makeFromStream( mpool, stream, &game->game, + globals->gi, dict, + globals->util, globals->dctx, &cp, + NULL ); + stream_destroy( stream ); + + setJGI( env, jgi, globals->gi ); + + LOG_RETURNF( "%s", result?"success":"failure" ); + return result; +} /* makeFromStream */ + +JNIEXPORT jbyteArray JNICALL +Java_org_eehouse_android_xw4_jni_XwJNI_game_1saveToStream +( JNIEnv* env, jclass C, jint gamePtr, jobject jgi ) +{ + GameAndMPool* game = (GameAndMPool*)gamePtr; + AndGlobals* globals = game->globals; + + CurGameInfo* gi = makeGI( MPPARM(game->mpool) env, jgi ); + XWStreamCtxt* stream = mem_stream_make( game->mpool, globals->vtMgr, + NULL, 0, NULL ); + game_saveToStream( &game->game, gi, stream ); + destroyGI( MPPARM(game->mpool) gi ); + + int nBytes = stream_getSize( stream ); + jbyteArray jarr = (*env)->NewByteArray( env, nBytes ); + jbyte* jelems = (*env)->GetByteArrayElements( env, jarr, NULL ); + stream_getBytes( stream, jelems, nBytes ); + (*env)->ReleaseByteArrayElements( env, jarr, jelems, 0 ); + stream_destroy( stream ); + + (*env)->DeleteLocalRef( env, jarr ); + return jarr; +} + JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose ( JNIEnv * env, jclass claz, jint gamePtr ) {