add basic framework to collect metadata prior to saving open game

(e.g. number of moves) and display it in game list.  What to save and
how to display it still not finalized but it works.
This commit is contained in:
eehouse 2010-04-08 04:09:50 +00:00
parent aa8b6ac7e6
commit 812262e41d
9 changed files with 222 additions and 22 deletions

View file

@ -917,6 +917,20 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1receiveMessage
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_game_1summarize
( JNIEnv* env, jclass C, jint gamePtr, jobject jsummary )
{
LOG_FUNC();
XWJNI_START();
XP_S16 nMoves = model_getNMoves( state->game.model );
setInt( env, jsummary, "nMoves", nMoves );
setBool( env, jsummary, "gameOver",
server_getGameIsOver( state->game.server ) );
XWJNI_END();
LOG_RETURN_VOID();
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1prefsChanged
( JNIEnv* env, jclass C, jint gamePtr, jobject jcp )

View file

@ -351,6 +351,13 @@ public class BoardActivity extends Activity implements UtilCtxt {
return super.onKeyUp( keyCode, event );
}
// onDestroy may be the wrong place to do this.
// onWindowFocusChanged seems to be getting called in GamesList
// before this function, and so the list item corresponding to
// this game isn't necessarily up-to-date. But if move e.g. to
// onStop() then onStart() needs to be prepared to reconstitute
// everything.
@Override
protected void onDestroy()
{
@ -366,8 +373,11 @@ public class BoardActivity extends Activity implements UtilCtxt {
// This has to happen after the drawing thread is killed
// to avoid the possibility of reentering the jni world.
GameSummary summary = new GameSummary();
XwJNI.game_summarize( m_jniGamePtr, summary );
byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, null );
Utils.saveGame( this, state, m_path );
Utils.saveSummary( m_path, summary );
XwJNI.game_dispose( m_jniGamePtr );
m_jniGamePtr = 0;

View file

@ -0,0 +1,59 @@
/* -*- compile-command: "cd ../../../../../; ant install"; -*- */
/*
* Copyright 2009-2010 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.eehouse.android.xw4;
import android.content.Context;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
public class DBHelper extends SQLiteOpenHelper {
public static final String TABLE_NAME = "summaries";
private static final String DB_NAME = "xwdb";
private static final int DB_VERSION = 1;
public static final String FILE_NAME = "FILE_NAME";
public static final String NUM_MOVES = "NUM_MOVES";
public static final String GAME_OVER = "GAME_OVER";
public static final String SNAPSHOT = "SNAPSHOT";
public DBHelper( Context context )
{
super( context, DB_NAME, null, DB_VERSION );
}
@Override
public void onCreate( SQLiteDatabase db )
{
db.execSQL( "CREATE TABLE " + TABLE_NAME + " ("
+ FILE_NAME + " TEXT PRIMARY KEY,"
+ NUM_MOVES + " INTEGER,"
+ GAME_OVER + " INTEGER,"
+ SNAPSHOT + " BLOB,"
+ ");" );
}
@Override
public void onUpgrade( SQLiteDatabase db, int oldVersion, int newVersion )
{
Utils.logf( "onUpgrade: old: %d; new: %d", oldVersion, newVersion );
}
}

View file

@ -522,8 +522,8 @@ public class GameConfig extends Activity implements View.OnClickListener {
boolean consumed = false;
if ( keyCode == KeyEvent.KEYCODE_BACK ) {
saveChanges();
if ( 0 < m_nMoves && (m_giOrig.changesMatter(m_gi)
|| m_carOrig.changesMatter(m_car) ) ) {
if ( 0 <= m_nMoves && (m_giOrig.changesMatter(m_gi)
|| m_carOrig.changesMatter(m_car) ) ) {
showDialog( CONFIRM_CHANGE );
consumed = true;
} else {
@ -782,6 +782,11 @@ public class GameConfig extends Activity implements View.OnClickListener {
}
Utils.saveGame( this, gamePtr, m_gi, m_path );
GameSummary summary = new GameSummary();
XwJNI.game_summarize( gamePtr, summary );
Utils.saveSummary( m_path, summary );
XwJNI.game_dispose( gamePtr );
}

View file

@ -51,14 +51,35 @@ public class GameListAdapter implements ListAdapter {
return Utils.gamesList(m_context).length;
}
public Object getItem( int position ) {
public Object getItem( int position )
{
TextView view = new TextView(m_context);
byte[] stream = open( Utils.gamesList(m_context)[position] );
String path = Utils.gamesList(m_context)[position];
byte[] stream = open( path );
if ( null != stream ) {
CurGameInfo gi = new CurGameInfo( m_context );
XwJNI.gi_from_stream( gi, stream );
view.setText( gi.summary(m_context) );
String summaryTxt = gi.summary( m_context );
GameSummary summary = Utils.getSummary( m_context, path );
if ( null != summary ) {
String state = "\nState: ";
if ( summary.nMoves < 0 ) {
state += "Configured";
} else if ( summary.gameOver ) {
state += "Game over";
} else {
state += "In play";
}
summaryTxt += state;
if ( summary.nMoves >= 0 ) {
summaryTxt += String.format( " Moves played: %d",
summary.nMoves );
}
}
view.setText( summaryTxt );
}
return view;
}

View file

@ -42,8 +42,6 @@ import junit.framework.Assert;
import org.eehouse.android.xw4.jni.*;
import org.eehouse.android.xw4.XWords4.Games; // for constants
public class GamesList extends ListActivity implements View.OnClickListener {
private GameListAdapter m_adapter;
@ -102,13 +100,6 @@ public class GamesList extends ListActivity implements View.OnClickListener {
Button newGameB = (Button)findViewById(R.id.new_game);
newGameB.setOnClickListener( this );
// If no data was given in the intent (because we were started
// as a MAIN activity), then use our default content provider.
Intent intent = getIntent();
if (intent.getData() == null) {
intent.setData(Games.CONTENT_URI);
}
m_adapter = new GameListAdapter( this );
setListAdapter( m_adapter );
}
@ -124,7 +115,8 @@ public class GamesList extends ListActivity implements View.OnClickListener {
@Override
public void onCreateContextMenu( ContextMenu menu, View view,
ContextMenuInfo menuInfo ) {
ContextMenuInfo menuInfo )
{
MenuInflater inflater = getMenuInflater();
inflater.inflate( R.menu.games_list_item_menu, menu );
}
@ -147,6 +139,7 @@ public class GamesList extends ListActivity implements View.OnClickListener {
int id = item.getItemId();
if ( R.id.list_item_delete == id ) {
Utils.saveSummary( path, null );
if ( ! deleteFile( path ) ) {
Utils.logf( "unable to delete " + path );
}
@ -169,14 +162,17 @@ public class GamesList extends ListActivity implements View.OnClickListener {
case R.id.list_item_reset:
Utils.resetGame( this, path, path );
Utils.saveSummary( path, null );
break;
case R.id.list_item_new_from:
Utils.resetGame( this, path );
String newName = Utils.resetGame( this, path );
Utils.saveSummary( newName, null );
break;
case R.id.list_item_copy:
stream = Utils.savedGame( this, path );
Utils.saveGame( this, stream );
newName = Utils.saveGame( this, stream );
Utils.saveSummary( newName, Utils.getSummary( this, path ) );
break;
// These require some notion of predictable sort order.

View file

@ -32,6 +32,7 @@ import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.util.ArrayList;
import android.content.res.AssetManager;
import android.content.ContentValues;
import android.os.Environment;
import java.io.InputStream;
import android.widget.CheckBox;
@ -46,6 +47,9 @@ import java.util.Formatter;
import android.view.LayoutInflater;
import android.net.Uri;
import junit.framework.Assert;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.Cursor;
import org.eehouse.android.xw4.jni.*;
@ -54,8 +58,10 @@ public class Utils {
static final int DIALOG_ABOUT = 1;
static final int DIALOG_LAST = DIALOG_ABOUT;
static final String DB_PATH = "XW_GAMES";
private static Time s_time = new Time();
private static SQLiteOpenHelper m_dbHelper = null;
private Utils() {}
@ -164,9 +170,11 @@ public class Utils {
return al.toArray( new String[al.size()] );
}
public static void resetGame( Context context, String pathIn )
public static String resetGame( Context context, String pathIn )
{
resetGame( context, pathIn, newName( context ) );
String newName = newName( context );
resetGame( context, pathIn, newName );
return newName;
}
public static void loadMakeGame( Context context, int gamePtr,
@ -211,9 +219,11 @@ public class Utils {
}
}
public static void saveGame( Context context, byte[] bytes )
public static String saveGame( Context context, byte[] bytes )
{
saveGame( context, bytes, newName( context ) );
String name = newName( context );
saveGame( context, bytes, name );
return name;
}
public static boolean gameDictHere( Context context, String path,
@ -436,6 +446,30 @@ public class Utils {
}
}
public static GameSummary getSummary( Context context, String file )
{
initDB( context );
GameSummary summary = new GameSummary();
SQLiteDatabase db = m_dbHelper.getReadableDatabase();
String[] columns = { DBHelper.NUM_MOVES, DBHelper.GAME_OVER };
String selection = DBHelper.FILE_NAME + "=\"" + file + "\"";
Cursor cursor = db.query( DBHelper.TABLE_NAME, columns, selection,
null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
summary = new GameSummary();
summary.nMoves = cursor.getInt(cursor.
getColumnIndex(DBHelper.NUM_MOVES));
int tmp = cursor.getInt(cursor.
getColumnIndex(DBHelper.GAME_OVER));
summary.gameOver = tmp == 0 ? false : true;
}
db.close();
return summary;
}
private static boolean isGame( String file )
{
return file.endsWith( XWConstants.GAME_EXTN );
@ -446,4 +480,35 @@ public class Utils {
return file.endsWith( XWConstants.DICT_EXTN );
}
private static void initDB( Context context )
{
if ( null == m_dbHelper ) {
m_dbHelper = new DBHelper( context );
}
}
public static void saveSummary( String path, GameSummary summary )
{
SQLiteDatabase db = m_dbHelper.getWritableDatabase();
if ( null == summary ) {
String selection = DBHelper.FILE_NAME + "=\"" + path + "\"";
db.delete( DBHelper.TABLE_NAME, selection, null );
} else {
ContentValues values = new ContentValues();
values.put( DBHelper.FILE_NAME, path );
values.put( DBHelper.NUM_MOVES, summary.nMoves );
values.put( DBHelper.GAME_OVER, summary.gameOver );
Utils.logf( "saveSummary: nMoves=%d", summary.nMoves );
try {
long result = db.replaceOrThrow( DBHelper.TABLE_NAME, "", values );
} catch ( Exception ex ) {
Utils.logf( "ex: %s", ex.toString() );
}
}
db.close();
}
}

View file

@ -0,0 +1,29 @@
/* -*- compile-command: "cd ../../../../../../; ant install"; -*- */
/*
* Copyright 2009-2010 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.eehouse.android.xw4.jni;
/** Info we want to access when the game's closed that's not available
* in CurGameInfo
*/
public class GameSummary {
public int nMoves;
public boolean gameOver;
}

View file

@ -84,8 +84,9 @@ public class XwJNI {
public static native boolean game_receiveMessage( int gamePtr,
byte[] stream );
public static native void game_summarize( int gamePtr, GameSummary summary );
public static native byte[] game_saveToStream( int gamePtr,
CurGameInfo gi );
CurGameInfo gi );
public static native boolean game_hasComms( int gamePtr );
public static native void game_dispose( int gamePtr );