From dc273c716248119fa64b8c7bce4b5151c6fc8e9f Mon Sep 17 00:00:00 2001 From: Andy2 Date: Mon, 15 Aug 2011 18:27:00 -0700 Subject: [PATCH 01/16] fix proxy msg handling to process more than one relayID per message. Formerly all but the first were dropped. --- xwords4/relay/xwrelay.cpp | 69 ++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/xwords4/relay/xwrelay.cpp b/xwords4/relay/xwrelay.cpp index 3083087b6..67e8aae28 100644 --- a/xwords4/relay/xwrelay.cpp +++ b/xwords4/relay/xwrelay.cpp @@ -176,15 +176,28 @@ cmdToStr( XWRELAY_Cmd cmd ) } static bool -parseRelayID( const char* const in, char* buf, HostID* hid ) +parseRelayID( unsigned char** const inp, const unsigned char* const end, + char* buf, int buflen, HostID* hid ) { - const char* hidp = strrchr( (char*)in, '/' ); + const char* hidp = strchr( (char*)*inp, '/' ); + bool ok = NULL != hidp; + int connNameLen; + if ( ok ) { - int connNameLen = hidp - in; - strncpy( buf, in, connNameLen ); + connNameLen = hidp - (char*)*inp; + ok = connNameLen < buflen; + } + if ( ok ) { + strncpy( buf, (char*)*inp, connNameLen ); buf[connNameLen] = '\0'; *hid = atoi( hidp+1 ); + char* endptr; + *hid = strtol( hidp + 1, &endptr, 10 ); + if ( '\n' == *endptr ) { + ++endptr; + } + *inp = (unsigned char*)endptr; } return ok; } @@ -728,43 +741,42 @@ handleMsgsMsg( int sock, bool sendFull, unsigned char* bufp, const unsigned char* end ) { unsigned short nameCount; + int ii; if ( getNetShort( &bufp, end, &nameCount ) ) { - char* in = (char*)bufp; DBMgr* dbmgr = DBMgr::Get(); - unsigned short count; - /* This is wrong now */ - /* reply format: PRX_GET_MSGS case: []* - * PRX_HAS_MSGS case: * - */ vector out(4); /* space for len and n_msgs */ assert( out.size() == 4 ); + for ( ii = 0; ii < nameCount && bufp < end; ++ii ) { - char* saveptr; - for ( count = 0; ; ++count ) { - char* name = strtok_r( in, "\n", &saveptr ); - if ( NULL == name ) { - break; - } + // See NetUtils.java for reply format + // message-length: 2 + // nameCount: 2 + // name count reps of: + // counts-this-name: 2 + // counts-this-name reps of + // len: 2 + // msg: + + // pack msgs for one game HostID hid; char connName[MAX_CONNNAME_LEN+1]; - if ( !parseRelayID( name, connName, &hid ) ) { + if ( !parseRelayID( &bufp, end, connName, sizeof(connName), + &hid ) ) { break; } - /* For each relayID, write the number of messages and then each - message (in the getmsg case) */ + /* For each relayID, write the number of messages and then + each message (in the getmsg case) */ int msgCount = dbmgr->PendingMsgCount( connName, hid ); pushShort( out, msgCount ); if ( sendFull ) { pushMsgs( out, dbmgr, connName, hid, msgCount ); } - - in = NULL; } unsigned short tmp = htons( out.size() - sizeof(tmp) ); memcpy( &out[0], &tmp, sizeof(tmp) ); - tmp = htons( count ); + tmp = htons( nameCount ); memcpy( &out[2], &tmp, sizeof(tmp) ); write( sock, &out[0], out.size() ); } @@ -817,24 +829,15 @@ handle_proxy_packet( unsigned char* buf, int len, int sock ) if ( !getNetShort( &bufp, end, &seed ) ) { break; } - unsigned char* crptr = - (unsigned char*)strchr( (char*)bufp, '\n' ); - if ( NULL == crptr || crptr >= end ) { - break; - } - *crptr = '\0'; HostID hid; char connName[MAX_CONNNAME_LEN+1]; - if ( !parseRelayID( (const char*)bufp, connName, - &hid ) ) { + if ( !parseRelayID( &bufp, end, connName, + sizeof( connName ), &hid ) ) { break; } SafeCref scr( connName ); scr.DeviceGone( hid, seed ); - - /* skip "\n" */ - bufp = crptr + 1; } } } From c2c673e93265b817e9db38092eb2f20f78a52d50 Mon Sep 17 00:00:00 2001 From: Eric House Date: Tue, 16 Aug 2011 19:44:37 -0700 Subject: [PATCH 02/16] improve testing of proxy delete by saving up a number of relayIDs and sending with multiple -d flags to rq. --- xwords4/linux/scripts/discon_ok2.sh | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/xwords4/linux/scripts/discon_ok2.sh b/xwords4/linux/scripts/discon_ok2.sh index 9508f9243..36bbca4a4 100755 --- a/xwords4/linux/scripts/discon_ok2.sh +++ b/xwords4/linux/scripts/discon_ok2.sh @@ -225,11 +225,17 @@ close_device() { unset ROOMS[$ID] } +OBITS="" + kill_from_log() { LOG=$1 RELAYID=$(./scripts/relayID.sh --long $LOG) if [ -n "$RELAYID" ]; then - ../relay/rq -a $HOST -d $RELAYID 2>/dev/null || true + OBITS="$OBITS -d $RELAYID" + if [ 0 -eq $(($RANDOM%2)) ]; then + ../relay/rq -a $HOST $OBITS 2>/dev/null || true + OBITS="" + fi return 0 # success fi echo "unable to send kill command for $LOG" @@ -281,10 +287,15 @@ check_game() { close_device $ID $DONEDIR "game over" done date + # XWRELAY_ERROR_DELETED may be old elif grep -q 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then echo "deleting $LOG $(connName $LOG) b/c another resigned" kill_from_log $LOG || true close_device $KEY $DEADDIR "other resigned" + elif grep -q 'relay_error_curses(XWRELAY_ERROR_DEADGAME)' $LOG; then + echo "deleting $LOG $(connName $LOG) b/c another resigned" + kill_from_log $LOG || true + close_device $KEY $DEADDIR "other resigned" else maybe_resign $KEY fi @@ -334,6 +345,8 @@ run_cmds() { fi done + [ -n "$OBITS" ] && ../relay/rq -a $HOST $OBITS 2>/dev/null || true + # kill any remaining games if [ $COUNT -gt 0 ]; then mkdir -p ${LOGDIR}/not_done From 0c29a4c1ca7d98d499e2be3f631234c0e7bcf3ea Mon Sep 17 00:00:00 2001 From: Andy2 Date: Wed, 17 Aug 2011 20:11:30 -0700 Subject: [PATCH 03/16] fix NPE when sd card unmounted --- .../android/XWords4/src/org/eehouse/android/xw4/GameUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index f1aed07e5..03b975378 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -718,7 +718,7 @@ public class GameUtils { File file = context.getFileStreamPath( name ); if ( !file.exists() ) { file = getSDPathFor( context, name ); - if ( !file.exists() ) { + if ( null != file && !file.exists() ) { file = null; } } From 455cb9a4c44fbb134ef43a203806c864f092a84a Mon Sep 17 00:00:00 2001 From: Andy2 Date: Thu, 18 Aug 2011 06:26:43 -0700 Subject: [PATCH 04/16] Include possibility of umounted sd card in missing dict explanation. Name of game too. Use single-selectable list and Substitute button instead of expecting user to tap once to select (which confused me.) --- .../android/XWords4/res/values/strings.xml | 12 ++--- .../org/eehouse/android/xw4/GamesList.java | 44 ++++++++++--------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index f47737116..41ca0d2c1 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -389,11 +389,13 @@ Substitute dictionary (wordcount) Substitute - Unable to open game because no %s - dictionary found. - Unable to open game because - dictionary %1$s not found. You can download a replacement or - substitute another %2$s dictionary. + Unable to open game \"%1$s\" because no + %2$s dictionary found. (It may have been deleted, or stored on + an an SD card that\'s been unmounted.) + Unable to open game \"%1$s\" because + dictionary %2$s not found. (It may have been deleted, or stored + on an an SD card that\'s been unmounted.) You can download a + replacement or substitute another %3$s dictionary. Password for \"%s\": diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java index 0b77b8353..e91a13658 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java @@ -97,14 +97,16 @@ public class GamesList extends XWListActivity } }; String message; - String langName = DictLangCache.getLangName( this, - m_missingDictLang ); + String langName = + DictLangCache.getLangName( this, m_missingDictLang ); + String gameName = GameUtils.getName( this, m_rowid ); if ( WARN_NODICT == id ) { - message = String.format( getString(R.string.no_dictf), - langName ); + message = String.format( getString( R.string.no_dictf ), + gameName, langName ); } else { message = String.format( getString(R.string.no_dict_substf), - m_missingDictNames[0], langName ); + gameName, m_missingDictNames[0], + langName ); } ab = new AlertDialog.Builder( this ) @@ -127,24 +129,26 @@ public class GamesList extends XWListActivity case SHOW_SUBST: m_sameLangDicts = DictLangCache.getHaveLangCounts( this, m_missingDictLang ); - ab = new AlertDialog.Builder( this ) + lstnr = new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dlg, + int which ) { + int pos = ((AlertDialog)dlg).getListView(). + getCheckedItemPosition(); + String dict = m_sameLangDicts[pos]; + dict = DictLangCache.stripCount( dict ); + GameUtils.replaceDicts( GamesList.this, + m_missingDictRowId, + m_missingDictNames[0], + dict ); + } + }; + dialog = new AlertDialog.Builder( this ) .setTitle( R.string.subst_dict_title ) + .setPositiveButton( R.string.button_substdict, lstnr ) .setNegativeButton( R.string.button_cancel, null ) - .setItems( m_sameLangDicts, - new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dlg, - int which ) { - String dict = m_sameLangDicts[which]; - dict = DictLangCache.stripCount( dict ); - GameUtils. - replaceDicts( GamesList.this, - m_missingDictRowId, - m_missingDictNames[0], - dict ); - } - }) + .setSingleChoiceItems( m_sameLangDicts, 0, null ) + .create(); ; - dialog = ab.create(); // Force destruction so onCreateDialog() will get // called next time and we can insert a different // list. There seems to be no way to change the list From 7a1346e530aa2e1a24a868e1882bdce2d3691562 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Thu, 18 Aug 2011 07:49:40 -0700 Subject: [PATCH 05/16] add assert to catch bad dict earlier --- xwords4/common/movestak.c | 1 + 1 file changed, 1 insertion(+) diff --git a/xwords4/common/movestak.c b/xwords4/common/movestak.c index 4bb44740b..ac2295fa2 100644 --- a/xwords4/common/movestak.c +++ b/xwords4/common/movestak.c @@ -61,6 +61,7 @@ void stack_setBitsPerTile( StackCtxt* stack, XP_U16 bitsPerTile ) { XP_ASSERT( !!stack ); + XP_ASSERT( bitsPerTile == 5 || bitsPerTile == 6 ); stack->bitsPerTile = bitsPerTile; } From fd4e6276286911182ee59256711b18a0cfba73c0 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Thu, 18 Aug 2011 07:54:00 -0700 Subject: [PATCH 06/16] when sd card is umounted, android sends a message to apps. I can't figure out how to get that so I ignore it and am killed then relaunched if I have a dict file open on the SD. On relauch, don't crash. Instead, check if any dicts used by the game is unreachable and put up an alert with only one choice: Close game. Thought about using the missing-dicts stuff from GamesList, but this is a special case that should be seen only when user umounts while a BoardActivity instance is frontmost. --- .../android/XWords4/res/values/strings.xml | 11 +- .../eehouse/android/xw4/BoardActivity.java | 172 ++++++++++-------- .../org/eehouse/android/xw4/GameUtils.java | 22 ++- 3 files changed, 125 insertions(+), 80 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 41ca0d2c1..ebd8ed481 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -389,13 +389,18 @@ Substitute dictionary (wordcount) Substitute + Close game + A dictionary this game is using has + disappeared. (Usually this means it\'s on an external card that + is no longer available.) Unable to open game \"%1$s\" because no %2$s dictionary found. (It may have been deleted, or stored on - an an SD card that\'s been unmounted.) + an external card that is no longer available.) Unable to open game \"%1$s\" because dictionary %2$s not found. (It may have been deleted, or stored - on an an SD card that\'s been unmounted.) You can download a - replacement or substitute another %3$s dictionary. + on an external card that is no longer available.) You can + download a replacement or substitute another %3$s + dictionary. Password for \"%s\": 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 a79c25951..94bfe9947 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -38,6 +38,7 @@ import android.app.Dialog; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; +import android.content.DialogInterface.OnDismissListener; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.EditText; @@ -68,6 +69,7 @@ public class BoardActivity extends XWActivity private static final int QUERY_ENDGAME = DLG_OKONLY + 7; private static final int DLG_DELETED = DLG_OKONLY + 8; private static final int DLG_INVITE = DLG_OKONLY + 9; + private static final int DLG_NODICT = DLG_OKONLY + 10; private static final int CHAT_REQUEST = 1; private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins @@ -80,7 +82,7 @@ public class BoardActivity extends XWActivity private int m_jniGamePtr; private GameUtils.GameLock m_gameLock; private CurGameInfo m_gi; - CommsTransport m_xport; + private CommsTransport m_xport; private Handler m_handler = null; private TimerRunnable[] m_timers; private Runnable m_screenTimer; @@ -286,6 +288,21 @@ public class BoardActivity extends XWActivity .create(); } break; + case DLG_NODICT: + dialog = new AlertDialog.Builder( this ) + .setTitle( R.string.no_dict_title ) + .setMessage( R.string.no_dict_finish ) + .setPositiveButton( R.string.button_close_game, null ) + .create(); + OnDismissListener dlstnr; + dlstnr = new OnDismissListener() { + public void onDismiss( DialogInterface di ) { + // removeDialog( DLG_NODICT ); + finish(); + } + }; + dialog.setOnDismissListener( dlstnr ); + break; default: // just drop it; super.onCreateDialog likely failed break; @@ -1044,89 +1061,96 @@ public class BoardActivity extends XWActivity private void loadGame() { if ( 0 == m_jniGamePtr ) { - Assert.assertNull( m_gameLock ); - m_gameLock = new GameUtils.GameLock( m_rowid, true ).lock(); - - byte[] stream = GameUtils.savedGame( this, m_gameLock ); - XwJNI.gi_from_stream( m_gi, stream ); - String[] dictNames = m_gi.dictNames(); GameUtils.DictPairs pairs = GameUtils.openDicts( this, dictNames ); - String langName = m_gi.langName(); - m_jniGamePtr = XwJNI.initJNI(); - if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) { - m_xport = new CommsTransport( m_jniGamePtr, this, this, - m_gi.serverRole ); - } + if ( pairs.anyMissing( dictNames ) ) { + showDialog( DLG_NODICT ); + } else { - CommonPrefs cp = CommonPrefs.get( this ); - if ( null == stream || - ! XwJNI.game_makeFromStream( m_jniGamePtr, stream, - m_gi, dictNames, pairs.m_bytes, - pairs.m_paths, langName, m_utils, - m_jniu, m_view, cp, m_xport ) ) { - XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, m_jniu, - m_view, cp, m_xport, dictNames, - pairs.m_bytes, pairs.m_paths, - langName ); - } + String langName = m_gi.langName(); - m_jniThread = new - JNIThread( m_jniGamePtr, m_gi, m_view, m_gameLock, this, - new Handler() { - public void handleMessage( Message msg ) { - switch( msg.what ) { - case JNIThread.DRAW: - m_view.invalidate(); - break; - case JNIThread.DIALOG: - m_dlgBytes = (String)msg.obj; - m_dlgTitle = msg.arg1; - showDialog( DLG_OKONLY ); - break; - case JNIThread.QUERY_ENDGAME: - showDialog( QUERY_ENDGAME ); - break; - case JNIThread.TOOLBAR_STATES: - if ( null != m_jniThread ) { - m_gsi = m_jniThread.getGameStateInfo(); - updateToolbar(); + Assert.assertNull( m_gameLock ); + m_gameLock = new GameUtils.GameLock( m_rowid, true ).lock(); + + byte[] stream = GameUtils.savedGame( this, m_gameLock ); + XwJNI.gi_from_stream( m_gi, stream ); + + m_jniGamePtr = XwJNI.initJNI(); + + if ( m_gi.serverRole != DeviceRole.SERVER_STANDALONE ) { + m_xport = new CommsTransport( m_jniGamePtr, this, this, + m_gi.serverRole ); + } + + CommonPrefs cp = CommonPrefs.get( this ); + if ( null == stream || + ! XwJNI.game_makeFromStream( m_jniGamePtr, stream, + m_gi, dictNames, pairs.m_bytes, + pairs.m_paths, langName, m_utils, + m_jniu, m_view, cp, m_xport ) ) { + XwJNI.game_makeNewGame( m_jniGamePtr, m_gi, m_utils, m_jniu, + m_view, cp, m_xport, dictNames, + pairs.m_bytes, pairs.m_paths, + langName ); + } + + m_jniThread = new + JNIThread( m_jniGamePtr, m_gi, m_view, m_gameLock, this, + new Handler() { + public void handleMessage( Message msg ) { + switch( msg.what ) { + case JNIThread.DRAW: + m_view.invalidate(); + break; + case JNIThread.DIALOG: + m_dlgBytes = (String)msg.obj; + m_dlgTitle = msg.arg1; + showDialog( DLG_OKONLY ); + break; + case JNIThread.QUERY_ENDGAME: + showDialog( QUERY_ENDGAME ); + break; + case JNIThread.TOOLBAR_STATES: + if ( null != m_jniThread ) { + m_gsi = m_jniThread.getGameStateInfo(); + updateToolbar(); + } + break; } - break; } - } - } ); - // see http://stackoverflow.com/questions/680180/where-to-stop-\ - // destroy-threads-in-android-service-class - m_jniThread.setDaemon( true ); - m_jniThread.start(); + } ); + // see http://stackoverflow.com/questions/680180/where-to-stop-\ + // destroy-threads-in-android-service-class + m_jniThread.setDaemon( true ); + m_jniThread.start(); - m_view.startHandling( this, m_jniThread, m_jniGamePtr, m_gi ); - if ( null != m_xport ) { - m_xport.setReceiver( m_jniThread ); - } - m_jniThread.handle( JNICmd.CMD_START ); + m_view.startHandling( this, m_jniThread, m_jniGamePtr, m_gi ); + if ( null != m_xport ) { + m_xport.setReceiver( m_jniThread ); + } + m_jniThread.handle( JNICmd.CMD_START ); - if ( !CommonPrefs.getHideTitleBar( this ) ) { - setTitle( GameUtils.getName( this, m_rowid ) ); - } - m_toolbar = new Toolbar( this ); + if ( !CommonPrefs.getHideTitleBar( this ) ) { + setTitle( GameUtils.getName( this, m_rowid ) ); + } + m_toolbar = new Toolbar( this ); - populateToolbar(); + populateToolbar(); - int flags = DBUtils.getMsgFlags( this, m_rowid ); - if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) { - startChatActivity(); - } - if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) { - m_jniThread.handle( JNIThread.JNICmd.CMD_POST_OVER ); - } - if ( 0 != flags ) { - DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE ); - } + int flags = DBUtils.getMsgFlags( this, m_rowid ); + if ( 0 != (GameSummary.MSG_FLAGS_CHAT & flags) ) { + startChatActivity(); + } + if ( 0 != (GameSummary.MSG_FLAGS_GAMEOVER & flags) ) { + m_jniThread.handle( JNIThread.JNICmd.CMD_POST_OVER ); + } + if ( 0 != flags ) { + DBUtils.setMsgFlags( m_rowid, GameSummary.MSG_FLAGS_NONE ); + } - trySendChats(); + trySendChats(); + } } } // loadGame @@ -1209,9 +1233,9 @@ public class BoardActivity extends XWActivity }); } // populateToolbar - private DialogInterface.OnDismissListener makeODLforBlocking( final int id ) + private OnDismissListener makeODLforBlocking( final int id ) { - return new DialogInterface.OnDismissListener() { + return new OnDismissListener() { public void onDismiss( DialogInterface di ) { releaseIfBlocking(); removeDialog( id ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 03b975378..3381468af 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -158,7 +158,23 @@ public class GameUtils { public DictPairs( byte[][] bytes, String[] paths ) { m_bytes = bytes; m_paths = paths; } - } + + public boolean anyMissing( final String[] names ) + { + boolean missing = false; + for ( int ii = 0; ii < m_paths.length; ++ii ) { + if ( names[ii] != null ) { + // It's ok for there to be no dict IFF there's no + // name. That's a player using the default dict. + if ( null == m_paths[ii] && null == m_bytes[ii] ) { + missing = true; + break; + } + } + } + return missing; + } + } // DictPairs private static Object s_syncObj = new Object(); @@ -180,7 +196,7 @@ public class GameUtils { * basis for a new one. */ public static GameLock resetGame( Context context, GameLock lockSrc, - GameLock lockDest ) + GameLock lockDest ) { int gamePtr = XwJNI.initJNI(); CurGameInfo gi = new CurGameInfo( context ); @@ -897,7 +913,7 @@ public class GameUtils { // Which isn't possible right now, so make sure the old and new // dict have the same langauge code. public static void replaceDicts( Context context, long rowid, - String oldDict, String newDict ) + String oldDict, String newDict ) { GameLock lock = new GameLock( rowid, true ).lock(); byte[] stream = savedGame( context, lock ); From 8f60ca5af3e7e21842944cb831e829a141899a88 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Fri, 19 Aug 2011 18:52:55 -0700 Subject: [PATCH 07/16] Need to show dict-gone-missing dialog when umount from GameConfig too, so: change loadMakeGame() to return gamePtr rather than take one in, and to return 0 when any dict no available. Move dict-gone-missing dialog into DlgDelegate. Remove DlgDelegate always adding onDismiss listener that removes the dialog, and instead add one to the dict-gone-missing dialog that, like the onClick handler, calls finish() on the activity. --- .../eehouse/android/xw4/BoardActivity.java | 18 +-- .../org/eehouse/android/xw4/DlgDelegate.java | 35 +++- .../org/eehouse/android/xw4/GameConfig.java | 153 +++++++++--------- .../org/eehouse/android/xw4/GameUtils.java | 50 +++--- .../android/xw4/RelayGameActivity.java | 3 +- .../org/eehouse/android/xw4/XWActivity.java | 15 +- .../eehouse/android/xw4/XWListActivity.java | 1 + 7 files changed, 153 insertions(+), 122 deletions(-) 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 94bfe9947..436809680 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardActivity.java @@ -69,7 +69,6 @@ public class BoardActivity extends XWActivity private static final int QUERY_ENDGAME = DLG_OKONLY + 7; private static final int DLG_DELETED = DLG_OKONLY + 8; private static final int DLG_INVITE = DLG_OKONLY + 9; - private static final int DLG_NODICT = DLG_OKONLY + 10; private static final int CHAT_REQUEST = 1; private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins @@ -288,21 +287,6 @@ public class BoardActivity extends XWActivity .create(); } break; - case DLG_NODICT: - dialog = new AlertDialog.Builder( this ) - .setTitle( R.string.no_dict_title ) - .setMessage( R.string.no_dict_finish ) - .setPositiveButton( R.string.button_close_game, null ) - .create(); - OnDismissListener dlstnr; - dlstnr = new OnDismissListener() { - public void onDismiss( DialogInterface di ) { - // removeDialog( DLG_NODICT ); - finish(); - } - }; - dialog.setOnDismissListener( dlstnr ); - break; default: // just drop it; super.onCreateDialog likely failed break; @@ -1065,7 +1049,7 @@ public class BoardActivity extends XWActivity GameUtils.DictPairs pairs = GameUtils.openDicts( this, dictNames ); if ( pairs.anyMissing( dictNames ) ) { - showDialog( DLG_NODICT ); + showDictGoneFinish(); } else { String langName = m_gi.langName(); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java index 67e3a3193..8f91ed2d1 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java @@ -40,7 +40,8 @@ public class DlgDelegate { public static final int DIALOG_NOTAGAIN = 3; public static final int CONFIRM_THEN = 4; public static final int TEXT_OR_HTML_THEN = 5; - public static final int DIALOG_LAST = TEXT_OR_HTML_THEN; + public static final int DLG_DICTGONE = 6; + public static final int DIALOG_LAST = DLG_DICTGONE; private int m_msgID; private String m_msg; @@ -78,6 +79,9 @@ public class DlgDelegate { case TEXT_OR_HTML_THEN: dialog = createHtmlThenDialog(); break; + case DLG_DICTGONE: + dialog = createDictGoneDialog(); + break; } return dialog; } @@ -141,6 +145,11 @@ public class DlgDelegate { m_activity.showDialog( DIALOG_OKONLY ); } + public void showDictGoneFinish() + { + m_activity.showDialog( DLG_DICTGONE ); + } + public void showAboutDialog() { m_activity.showDialog( DIALOG_ABOUT ); @@ -283,4 +292,28 @@ public class DlgDelegate { .create(); } + private Dialog createDictGoneDialog() + { + Utils.logf( "DlgDelegate.createDictGoneDialog() called" ); + Dialog dialog; + dialog = new AlertDialog.Builder( m_activity ) + .setTitle( R.string.no_dict_title ) + .setMessage( R.string.no_dict_finish ) + .setPositiveButton( R.string.button_close_game, + new DialogInterface.OnClickListener() { + public void onClick( DialogInterface dlg, int item ) { + m_activity.finish(); + } + } ) + .create(); + + dialog.setOnDismissListener( new DialogInterface.OnDismissListener() { + public void onDismiss( DialogInterface di ) { + m_activity.finish(); + } + } ); + + return dialog; + } + } 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 0f5e18f6e..b007ee2ae 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameConfig.java @@ -396,88 +396,91 @@ public class GameConfig extends XWActivity { super.onResume(); - int gamePtr = XwJNI.initJNI(); m_giOrig = new CurGameInfo( this ); // Lock in case we're going to config. We *could* re-get the // lock once the user decides to make changes. PENDING. m_gameLock = new GameUtils.GameLock( m_rowid, true ).lock(); - GameUtils.loadMakeGame( this, gamePtr, m_giOrig, m_gameLock ); - m_gameStarted = XwJNI.model_getNMoves( gamePtr ) > 0 - || XwJNI.comms_isConnected( gamePtr ); - - if ( m_gameStarted ) { - if ( null == m_gameLockedCheck ) { - m_gameLockedCheck = - (CheckBox)findViewById( R.id.game_locked_check ); - m_gameLockedCheck.setVisibility( View.VISIBLE ); - m_gameLockedCheck.setChecked( true ); - m_gameLockedCheck.setOnClickListener( this ); - } - handleLockedChange(); - } - - m_gi = new CurGameInfo( this, m_giOrig ); - - m_carOrig = new CommsAddrRec( this ); - if ( XwJNI.game_hasComms( gamePtr ) ) { - XwJNI.comms_getAddr( gamePtr, m_carOrig ); + int gamePtr = GameUtils.loadMakeGame( this, m_giOrig, m_gameLock ); + if ( 0 == gamePtr ) { + showDictGoneFinish(); } else { - String relayName = CommonPrefs.getDefaultRelayHost( this ); - int relayPort = CommonPrefs.getDefaultRelayPort( this ); - XwJNI.comms_getInitialAddr( m_carOrig, relayName, relayPort ); - } - XwJNI.game_dispose( gamePtr ); + m_gameStarted = XwJNI.model_getNMoves( gamePtr ) > 0 + || XwJNI.comms_isConnected( gamePtr ); - m_car = new CommsAddrRec( m_carOrig ); - - m_notNetworkedGame = DeviceRole.SERVER_STANDALONE == m_gi.serverRole; - setTitle(); - - if ( !m_notNetworkedGame ) { - m_joinPublicCheck = - (CheckBox)findViewById(R.id.join_public_room_check); - m_joinPublicCheck.setOnClickListener( this ); - m_joinPublicCheck.setChecked( m_car.ip_relay_seeksPublicRoom ); - Utils.setChecked( this, R.id.advertise_new_room_check, - m_car.ip_relay_advertiseRoom ); - m_publicRoomsSet = - (LinearLayout)findViewById(R.id.public_rooms_set ); - m_privateRoomsSet = - (LinearLayout)findViewById(R.id.private_rooms_set ); - - Utils.setText( this, R.id.room_edit, m_car.ip_relay_invite ); - - m_roomChoose = (Spinner)findViewById( R.id.room_spinner ); - - m_refreshRoomsButton = - (ImageButton)findViewById( R.id.refresh_button ); - m_refreshRoomsButton.setOnClickListener( this ); - - adjustConnectStuff(); - } - - loadPlayers(); - configLangSpinner(); - - m_phoniesSpinner.setSelection( m_gi.phoniesAction.ordinal() ); - - setSmartnessSpinner(); - - Utils.setChecked( this, R.id.hints_allowed, !m_gi.hintsNotAllowed ); - Utils.setInt( this, R.id.timer_minutes_edit, - m_gi.gameSeconds/60/m_gi.nPlayers ); - - CheckBox check = (CheckBox)findViewById( R.id.use_timer ); - CompoundButton.OnCheckedChangeListener lstnr = - new CompoundButton.OnCheckedChangeListener() { - public void onCheckedChanged( CompoundButton buttonView, - boolean checked ) { - View view = findViewById( R.id.timer_set ); - view.setVisibility( checked ? View.VISIBLE : View.GONE ); + if ( m_gameStarted ) { + if ( null == m_gameLockedCheck ) { + m_gameLockedCheck = + (CheckBox)findViewById( R.id.game_locked_check ); + m_gameLockedCheck.setVisibility( View.VISIBLE ); + m_gameLockedCheck.setChecked( true ); + m_gameLockedCheck.setOnClickListener( this ); } - }; - check.setOnCheckedChangeListener( lstnr ); - Utils.setChecked( this, R.id.use_timer, m_gi.timerEnabled ); + handleLockedChange(); + } + + m_gi = new CurGameInfo( this, m_giOrig ); + + m_carOrig = new CommsAddrRec( this ); + if ( XwJNI.game_hasComms( gamePtr ) ) { + XwJNI.comms_getAddr( gamePtr, m_carOrig ); + } else { + String relayName = CommonPrefs.getDefaultRelayHost( this ); + int relayPort = CommonPrefs.getDefaultRelayPort( this ); + XwJNI.comms_getInitialAddr( m_carOrig, relayName, relayPort ); + } + XwJNI.game_dispose( gamePtr ); + + m_car = new CommsAddrRec( m_carOrig ); + + m_notNetworkedGame = DeviceRole.SERVER_STANDALONE == m_gi.serverRole; + setTitle(); + + if ( !m_notNetworkedGame ) { + m_joinPublicCheck = + (CheckBox)findViewById(R.id.join_public_room_check); + m_joinPublicCheck.setOnClickListener( this ); + m_joinPublicCheck.setChecked( m_car.ip_relay_seeksPublicRoom ); + Utils.setChecked( this, R.id.advertise_new_room_check, + m_car.ip_relay_advertiseRoom ); + m_publicRoomsSet = + (LinearLayout)findViewById(R.id.public_rooms_set ); + m_privateRoomsSet = + (LinearLayout)findViewById(R.id.private_rooms_set ); + + Utils.setText( this, R.id.room_edit, m_car.ip_relay_invite ); + + m_roomChoose = (Spinner)findViewById( R.id.room_spinner ); + + m_refreshRoomsButton = + (ImageButton)findViewById( R.id.refresh_button ); + m_refreshRoomsButton.setOnClickListener( this ); + + adjustConnectStuff(); + } + + loadPlayers(); + configLangSpinner(); + + m_phoniesSpinner.setSelection( m_gi.phoniesAction.ordinal() ); + + setSmartnessSpinner(); + + Utils.setChecked( this, R.id.hints_allowed, !m_gi.hintsNotAllowed ); + Utils.setInt( this, R.id.timer_minutes_edit, + m_gi.gameSeconds/60/m_gi.nPlayers ); + + CheckBox check = (CheckBox)findViewById( R.id.use_timer ); + CompoundButton.OnCheckedChangeListener lstnr = + new CompoundButton.OnCheckedChangeListener() { + public void onCheckedChanged( CompoundButton buttonView, + boolean checked ) { + View view = findViewById( R.id.timer_set ); + view.setVisibility( checked ? View.VISIBLE : View.GONE ); + } + }; + check.setOnCheckedChangeListener( lstnr ); + Utils.setChecked( this, R.id.use_timer, m_gi.timerEnabled ); + } } // onResume @Override diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 3381468af..60e392920 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -198,13 +198,12 @@ public class GameUtils { public static GameLock resetGame( Context context, GameLock lockSrc, GameLock lockDest ) { - int gamePtr = XwJNI.initJNI(); CurGameInfo gi = new CurGameInfo( context ); CommsAddrRec addr = null; // loadMakeGame, if makinga new game, will add comms as long // as DeviceRole.SERVER_STANDALONE != gi.serverRole - loadMakeGame( context, gamePtr, gi, lockSrc ); + int gamePtr = loadMakeGame( context, gi, lockSrc ); String[] dictNames = gi.dictNames(); DictPairs pairs = openDicts( context, dictNames ); @@ -282,9 +281,8 @@ public class GameUtils { public static GameSummary summarize( Context context, GameLock lock ) { - int gamePtr = XwJNI.initJNI(); CurGameInfo gi = new CurGameInfo( context ); - loadMakeGame( context, gamePtr, gi, lock ); + int gamePtr = loadMakeGame( context, gi, lock ); return summarizeAndClose( context, lock, gamePtr, gi ); } @@ -321,33 +319,40 @@ public class GameUtils { return result; } - public static void loadMakeGame( Context context, int gamePtr, - CurGameInfo gi, GameLock lock ) + public static int loadMakeGame( Context context, CurGameInfo gi, + GameLock lock ) { - loadMakeGame( context, gamePtr, gi, null, lock ); + return loadMakeGame( context, gi, null, lock ); } - public static void loadMakeGame( Context context, int gamePtr, - CurGameInfo gi, UtilCtxt util, - GameLock lock ) + public static int loadMakeGame( Context context, CurGameInfo gi, + UtilCtxt util, GameLock lock ) { + int gamePtr = 0; + byte[] stream = savedGame( context, lock ); XwJNI.gi_from_stream( gi, stream ); String[] dictNames = gi.dictNames(); DictPairs pairs = openDicts( context, dictNames ); - String langName = gi.langName(); + if ( pairs.anyMissing( dictNames ) ) { + Utils.logf( "loadMakeGame() failing: dict unavailable" ); + } else { + gamePtr = XwJNI.initJNI(); - boolean madeGame = XwJNI.game_makeFromStream( gamePtr, stream, - JNIUtilsImpl.get(), gi, - dictNames, pairs.m_bytes, - pairs.m_paths, langName, - util, - CommonPrefs.get(context)); - if ( !madeGame ) { - XwJNI.game_makeNewGame( gamePtr, gi, JNIUtilsImpl.get(), - CommonPrefs.get(context), dictNames, - pairs.m_bytes, pairs.m_paths, langName ); + String langName = gi.langName(); + boolean madeGame = XwJNI.game_makeFromStream( gamePtr, stream, + JNIUtilsImpl.get(), gi, + dictNames, pairs.m_bytes, + pairs.m_paths, langName, + util, + CommonPrefs.get(context)); + if ( !madeGame ) { + XwJNI.game_makeNewGame( gamePtr, gi, JNIUtilsImpl.get(), + CommonPrefs.get(context), dictNames, + pairs.m_bytes, pairs.m_paths, langName ); + } } + return gamePtr; } public static long saveGame( Context context, int gamePtr, @@ -872,12 +877,11 @@ public class GameUtils { boolean draw = false; long rowid = DBUtils.getRowIDFor( context, relayID ); if ( -1 != rowid ) { - int gamePtr = XwJNI.initJNI(); CurGameInfo gi = new CurGameInfo( context ); FeedUtilsImpl feedImpl = new FeedUtilsImpl( context, rowid ); GameLock lock = new GameLock( rowid, true ); if ( lock.tryLock() ) { - loadMakeGame( context, gamePtr, gi, feedImpl, lock ); + int gamePtr = loadMakeGame( context, gi, feedImpl, lock ); for ( byte[] msg : msgs ) { draw = XwJNI.game_receiveMessage( gamePtr, msg ) || draw; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java index 1f0ee8d09..4f23b8ede 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayGameActivity.java @@ -67,10 +67,9 @@ public class RelayGameActivity extends XWActivity { super.onStart(); - int gamePtr = XwJNI.initJNI(); m_gi = new CurGameInfo( this ); m_gameLock = new GameUtils.GameLock( m_rowid, true ).lock(); - GameUtils.loadMakeGame( this, gamePtr, m_gi, m_gameLock ); + int gamePtr = GameUtils.loadMakeGame( this, m_gi, m_gameLock ); m_car = new CommsAddrRec( this ); if ( XwJNI.game_hasComms( gamePtr ) ) { XwJNI.comms_getAddr( gamePtr, m_car ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java index 7bb01a339..d094a9adf 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWActivity.java @@ -36,6 +36,7 @@ public class XWActivity extends Activity { private DlgDelegate m_delegate; + @Override protected void onCreate( Bundle savedInstanceState ) { Utils.logf( "%s.onCreate(this=%H)", getClass().getName(), this ); @@ -84,10 +85,10 @@ public class XWActivity extends Activity { @Override protected Dialog onCreateDialog( int id ) { - Utils.logf( "%s.onCreateDialog() called", getClass().getName() ); - Dialog dialog = m_delegate.onCreateDialog( id ); - if ( null != dialog ) { - setRemoveOnDismiss( dialog, id ); + Dialog dialog = super.onCreateDialog( id ); + if ( null == dialog ) { + Utils.logf( "%s.onCreateDialog() called", getClass().getName() ); + dialog = m_delegate.onCreateDialog( id ); } return dialog; } @@ -100,6 +101,7 @@ public class XWActivity extends Activity { @Override protected void onPrepareDialog( int id, Dialog dialog ) { + super.onPrepareDialog( id, dialog ); // docs say should call through m_delegate.onPrepareDialog( id, dialog ); } @@ -121,6 +123,11 @@ public class XWActivity extends Activity { m_delegate.showOKOnlyDialog( msgID ); } + protected void showDictGoneFinish() + { + m_delegate.showDictGoneFinish(); + } + protected void showConfirmThen( int msgID, DialogInterface.OnClickListener action ) { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListActivity.java index 77fe99799..bb1206eff 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/XWListActivity.java @@ -106,6 +106,7 @@ public class XWListActivity extends ListActivity { @Override protected void onPrepareDialog( int id, Dialog dialog ) { + super.onPrepareDialog( id, dialog ); m_delegate.onPrepareDialog( id, dialog ); } From 6ca60438a190c381e6218257740ebc967892588f Mon Sep 17 00:00:00 2001 From: Andy2 Date: Fri, 19 Aug 2011 18:54:21 -0700 Subject: [PATCH 08/16] no need for onClick listener when onDismiss listener does the same thing --- .../XWords4/src/org/eehouse/android/xw4/DlgDelegate.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java index 8f91ed2d1..abc2ee79f 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DlgDelegate.java @@ -299,12 +299,7 @@ public class DlgDelegate { dialog = new AlertDialog.Builder( m_activity ) .setTitle( R.string.no_dict_title ) .setMessage( R.string.no_dict_finish ) - .setPositiveButton( R.string.button_close_game, - new DialogInterface.OnClickListener() { - public void onClick( DialogInterface dlg, int item ) { - m_activity.finish(); - } - } ) + .setPositiveButton( R.string.button_close_game, null ) .create(); dialog.setOnDismissListener( new DialogInterface.OnDismissListener() { From 659c054696eacfa070677a7fd40467c99df565dd Mon Sep 17 00:00:00 2001 From: Andy2 Date: Sat, 20 Aug 2011 11:59:32 -0700 Subject: [PATCH 09/16] add new class that listens for SDCard mounted event (could be generalized for multiple events and map them to multiple notification callbacks), and use it from DictsActivity to redraw the list when the SD card comes back online. While the default android behavior works for the unmount case (relaunches DictsActivity which correctly draws only those dicts still available) it needs this change to redraw after the available set grows. --- .../eehouse/android/xw4/DictsActivity.java | 24 +++++-- .../eehouse/android/xw4/SDCardWatcher.java | 68 +++++++++++++++++++ 2 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 xwords4/android/XWords4/src/org/eehouse/android/xw4/SDCardWatcher.java diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java index 6901d3d7f..3cf294cf9 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java @@ -55,7 +55,8 @@ import org.eehouse.android.xw4.jni.JNIUtilsImpl; import org.eehouse.android.xw4.jni.CommonPrefs; public class DictsActivity extends ExpandableListActivity - implements View.OnClickListener, XWListItem.DeleteCallback { + implements View.OnClickListener, XWListItem.DeleteCallback, + SDCardWatcher.SDCardNotifiee { private static final String DICT_DOLAUNCH = "do_launch"; private static final String DICT_LANG_EXTRA = "use_lang"; @@ -75,9 +76,10 @@ public class DictsActivity extends ExpandableListActivity private XWListItem m_rowView; GameUtils.DictLoc m_moveFromLoc; GameUtils.DictLoc m_moveToLoc; - String m_moveName; + private SDCardWatcher m_cardWatcher; + private String m_moveName; - LayoutInflater m_factory; + private LayoutInflater m_factory; private class DictListAdapter implements ExpandableListAdapter { private Context m_context; @@ -331,10 +333,17 @@ public class DictsActivity extends ExpandableListActivity protected void onResume() { super.onResume(); + m_cardWatcher = new SDCardWatcher( this, this ); mkListAdapter(); expandGroups(); } + protected void onPause() { + m_cardWatcher.close(); + m_cardWatcher = null; + super.onPause(); + } + public void onClick( View v ) { askStartDownload( 0, null ); @@ -429,7 +438,7 @@ public class DictsActivity extends ExpandableListActivity showDialog( MOVE_DICT ); } - // DeleteCallback interface + // XWListItem.DeleteCallback interface public void deleteCalled( int myPosition, final String dict ) { int code = DictLangCache.getDictLangCode( this, dict ); @@ -457,6 +466,13 @@ public class DictsActivity extends ExpandableListActivity m_delegate.showConfirmThen( msg, action ); } + // SDCardWatcher.SDCardNotifiee interface + public void cardMounted() + { + mkListAdapter(); + expandGroups(); + } + private void deleteDict( String dict ) { GameUtils.deleteDict( this, dict ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SDCardWatcher.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SDCardWatcher.java new file mode 100644 index 000000000..1492e9731 --- /dev/null +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SDCardWatcher.java @@ -0,0 +1,68 @@ +/* -*- 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.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +public class SDCardWatcher { + public interface SDCardNotifiee { + void cardMounted(); + } + + private UmountReceiver m_rcvr; + private Context m_context; + private SDCardNotifiee m_notifiee; + + private class UmountReceiver extends BroadcastReceiver { + @Override + public void onReceive( Context context, Intent intent ) + { + if ( intent.getAction(). + equals( Intent.ACTION_MEDIA_MOUNTED ) ) { + m_notifiee.cardMounted(); + } + } + } + + public SDCardWatcher( Context context, SDCardNotifiee notifiee ) + { + m_context = context; + m_rcvr = new UmountReceiver(); + m_notifiee = notifiee; + + IntentFilter filter = + new IntentFilter( Intent.ACTION_MEDIA_MOUNTED ); + // filter.addAction( Intent.ACTION_MEDIA_UNMOUNTED ); + // filter.addAction( Intent.ACTION_MEDIA_EJECT ); + filter.addDataScheme( "file" ); + + /*Intent intent = */context.getApplicationContext(). + registerReceiver( m_rcvr, filter ); + } + + public void close() + { + m_context.getApplicationContext().unregisterReceiver( m_rcvr ); + } +} From 28c78a0a6a5c9167b0e30fa64bf2ed1e7dce9a42 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Sat, 20 Aug 2011 14:16:38 -0700 Subject: [PATCH 10/16] dictionary->wordlist --- .../android/XWords4/res/values/strings.xml | 62 +++++++++---------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index ebd8ed481..05ad8c121 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -25,10 +25,10 @@ Play game Crosswords - Dictionaries + Installed Crosswords Wordlists Languages (based on installed - dictionaries) + wordlists) Game language %s settings %s settings (networked) @@ -143,7 +143,7 @@ The hint feature is disabled for this game. Enable it for a new game using the Settings dialog. No peeking at remote players\' tiles! Refused attempt to register unexpected user[s] - Conflict between Host and Guest dictionaries; Host wins. + Conflict between Host and Guest wordlists; Host wins. At least one player must be marked \"Remote\" for a game started as Host. Off-device player @@ -161,18 +161,18 @@ Add player Shuffle players - Move dictionary + Move wordlist Make default Details - Move dictionary %1$s from %2$s to %3$s? + Move wordlist %1$s from %2$s to %3$s? For what players should this - dictionary (and the language %s) be the default for new - games? + wordlist be the default for new games? (The language %s will be + the default for both.) Human Robot Both - Dictionary (in %s) + Wordlist (in %s) Connection (via internet) Make new room public Join public room @@ -213,10 +213,10 @@ Are you sure you want to delete %s? \u0020It is the only %s - dictionary installed. One or more games will be unopenable + wordlist installed. One or more games will be unopenable without it. \u0020One game (at least) - is using it (but there is another %s dictionary installed that + is using it (but there is another %s wordlist installed that can replace it.) Allow hints @@ -254,8 +254,8 @@ Tapping on scoreboard name shows that player\'s tiles - Dictionary storage - Store downloaded dictionaries using + Wordlist storage + Store downloaded wordlists using internal or external (SD card) memory? (You can always move them later.) Internal @@ -270,7 +270,7 @@ Smartest robot Settings - Dictionaries + Wordlists Restore all Restore colors Are you sure you want to @@ -299,14 +299,14 @@ Fourth player Not used yet... - Dictionaries - Default dictionaries - Dictionary for humans - Dictionary for robots + Wordlists + Default wordlists + Wordlist for humans + Wordlist for robots Handle phonies How to handle \"phonies\" - (words not in dictionary) + (words not in wordlist) Board size Individual colors @@ -349,7 +349,7 @@ another device. You will not be able to play any further. - Word[s] %s not found in dictionary. + Word[s] %s not found in wordlist. Do you still want to accept this move? Turn lost. Illegal word[s] @@ -383,24 +383,24 @@ credits pending permission. Downloading Crosswords - dictionary %s... - Dictionary not found + wordlist %s... + Wordlist not found Download - Substitute dictionary (wordcount) + Substitute wordlist (wordcount) Substitute Close game - A dictionary this game is using has + A wordlist this game is using has disappeared. (Usually this means it\'s on an external card that is no longer available.) Unable to open game \"%1$s\" because no - %2$s dictionary found. (It may have been deleted, or stored on + %2$s wordlist found. (It may have been deleted, or stored on an external card that is no longer available.) Unable to open game \"%1$s\" because - dictionary %2$s not found. (It may have been deleted, or stored + wordlist %2$s not found. (It may have been deleted, or stored on an external card that is no longer available.) You can download a replacement or substitute another %3$s - dictionary. + wordlist. Password for \"%s\": @@ -513,12 +513,12 @@ game on the relay. You will be notified when the remaining device[s] have joined your room and play can begin. - Crosswords dictionaries, which are + Crosswords wordlists, which are just compressed lists of words plus tile information, determine - what language a game is played in and how \"smart\" the robot is. - You can download different sized dictionaries in many languages - here. Email me at eehouse@eehouse.org for information on building - and installing your own dictionaries. + what language a game is played in and how \"smart\" the robot + is. You can download different sized wordlists in many + languages here. Email me at eehouse@eehouse.org for information + on building and installing your own wordlists. Moving tiles to the board:\nYou can drag tiles between the rack and the board, or you can tap an From 47bdec4eda2503b2346dae32549b901833d6e60c Mon Sep 17 00:00:00 2001 From: Andy2 Date: Sat, 20 Aug 2011 21:51:57 -0700 Subject: [PATCH 11/16] pass context into GameSummary constructor rather than into methods that need it. No behavior change. --- .../src/org/eehouse/android/xw4/DBUtils.java | 4 +-- .../eehouse/android/xw4/GameListAdapter.java | 6 ++--- .../org/eehouse/android/xw4/GameUtils.java | 4 +-- .../eehouse/android/xw4/jni/GameSummary.java | 26 ++++++++++--------- .../eehouse/android/xw4/jni/JNIThread.java | 2 +- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java index 9e82f1c59..ca17f6a0a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DBUtils.java @@ -107,7 +107,7 @@ public class DBUtils { Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns, selection, null, null, null, null ); if ( 1 == cursor.getCount() && cursor.moveToFirst() ) { - summary = new GameSummary(); + summary = new GameSummary( context ); summary.nMoves = cursor.getInt(cursor. getColumnIndex(DBHelper.NUM_MOVES)); summary.nPlayers = @@ -223,7 +223,7 @@ public class DBUtils { values.put( DBHelper.TURN, summary.turn ); values.put( DBHelper.GIFLAGS, summary.giflags() ); values.put( DBHelper.PLAYERS, - summary.summarizePlayers(context) ); + summary.summarizePlayers() ); values.put( DBHelper.DICTLANG, summary.dictLang ); values.put( DBHelper.GAME_OVER, summary.gameOver ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index 1bc3b3f14..a8eb2d1dc 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -118,7 +118,7 @@ public class GameListAdapter extends XWListAdapter { } else { //Assert.assertNotNull( summary ); - String state = summary.summarizeState( m_context ); + String state = summary.summarizeState(); TextView view = (TextView)layout.findViewById( R.id.game_name ); if ( hideTitle ) { @@ -168,7 +168,7 @@ public class GameListAdapter extends XWListAdapter { View tmp = m_factory.inflate( R.layout.player_list_elem, null ); view = (TextView)tmp.findViewById( R.id.item_name ); - view.setText( summary.summarizePlayer( m_context, ii ) ); + view.setText( summary.summarizePlayer( ii ) ); view = (TextView)tmp.findViewById( R.id.item_score ); view.setText( String.format( " %d", summary.scores[ii] ) ); if ( summary.isNextToPlay( ii ) ) { @@ -200,7 +200,7 @@ public class GameListAdapter extends XWListAdapter { marker.setImageResource( iconID ); view = (TextView)layout.findViewById( R.id.role ); - String roleSummary = summary.summarizeRole( m_context ); + String roleSummary = summary.summarizeRole(); if ( null != roleSummary ) { view.setText( roleSummary ); } else { diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java index 60e392920..5eeec93a6 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -258,7 +258,7 @@ public class GameUtils { int gamePtr, CurGameInfo gi, FeedUtilsImpl feedImpl ) { - GameSummary summary = new GameSummary( gi ); + GameSummary summary = new GameSummary( context, gi ); XwJNI.game_summarize( gamePtr, summary ); if ( null != feedImpl ) { @@ -984,7 +984,7 @@ public class GameUtils { saveGame( context, gamePtr, gi, lock, false ); - GameSummary summary = new GameSummary( gi ); + GameSummary summary = new GameSummary( context, gi ); XwJNI.game_summarize( gamePtr, summary ); DBUtils.saveSummary( context, lock, summary ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java index 27b783dad..fc27bf958 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java @@ -59,14 +59,16 @@ public class GameSummary { private int m_giFlags; private String m_playersSummary; private CurGameInfo m_gi; + private Context m_context; - public GameSummary(){ + public GameSummary( Context context ) { + m_context = context; pendingMsgLevel = 0; } - public GameSummary( CurGameInfo gi ) + public GameSummary( Context context, CurGameInfo gi ) { - this(); + this( context ); nPlayers = gi.nPlayers; dictLang = gi.dictLang; serverRole = gi.serverRole; @@ -78,7 +80,7 @@ public class GameSummary { return null != relayID; } - public String summarizePlayers( Context context ) + public String summarizePlayers() { String result; if ( null == m_gi ) { @@ -110,25 +112,25 @@ public class GameSummary { m_playersSummary = summary; } - public String summarizeState( Context context ) + public String summarizeState() { String result = null; if ( gameOver ) { - result = context.getString( R.string.gameOver ); + result = m_context.getString( R.string.gameOver ); } else { - result = String.format( context.getString(R.string.movesf), + result = String.format( m_context.getString(R.string.movesf), nMoves ); } return result; } - public String summarizeRole( Context context ) + public String summarizeRole() { String result = null; if ( isRelayGame() ) { Assert.assertTrue( CommsAddrRec.CommsConnType.COMMS_CONN_RELAY == conType ); - String fmt = context.getString( R.string.summary_fmt_relay ); + String fmt = m_context.getString( R.string.summary_fmt_relay ); result = String.format( fmt, roomName ); } return result; @@ -175,14 +177,14 @@ public class GameSummary { m_giFlags = flags; } - public String summarizePlayer( Context context, int indx ) + public String summarizePlayer( int indx ) { String player = players[indx]; int formatID = 0; if ( !isLocal(indx) ) { boolean isMissing = 0 != ((1 << indx) & missingPlayers); if ( isMissing ) { - player = context.getString( R.string.missing_player ); + player = m_context.getString( R.string.missing_player ); } else { formatID = R.string.str_nonlocal_namef; } @@ -191,7 +193,7 @@ public class GameSummary { } if ( 0 != formatID ) { - String format = context.getString( formatID ); + String format = m_context.getString( formatID ); player = String.format( format, player ); } return player; 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 cd733cfef..e9241c973 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 @@ -264,7 +264,7 @@ public class JNIThread extends Thread { XwJNI.server_do( m_jniGamePtr ); XwJNI.game_getGi( m_jniGamePtr, m_gi ); - GameSummary summary = new GameSummary( m_gi ); + GameSummary summary = new GameSummary( m_context, m_gi ); XwJNI.game_summarize( m_jniGamePtr, summary ); byte[] state = XwJNI.game_saveToStream( m_jniGamePtr, null ); GameUtils.saveGame( m_context, state, m_lock, false ); From c41af221b74ca0f3ff959f2146d1e6d239efd33f Mon Sep 17 00:00:00 2001 From: Andy2 Date: Tue, 16 Aug 2011 19:17:47 -0700 Subject: [PATCH 12/16] fix non-debug compile warning --- xwords4/android/XWords4/jni/anddict.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/jni/anddict.c b/xwords4/android/XWords4/jni/anddict.c index b4bfb7ba8..1e6ffe3c7 100644 --- a/xwords4/android/XWords4/jni/anddict.c +++ b/xwords4/android/XWords4/jni/anddict.c @@ -398,7 +398,10 @@ and_dictionary_destroy( DictionaryCtxt* dict ) XP_FREEP( ctxt->super.mpool, &ctxt->super.langName ); if ( NULL == ctxt->byteArray ) { /* mmap case */ - int err = munmap( ctxt->bytes, ctxt->bytesSize ); +#ifdef DEBUG + int err = +#endif + munmap( ctxt->bytes, ctxt->bytesSize ); XP_ASSERT( 0 == err ); } else { (*env)->ReleaseByteArrayElements( env, ctxt->byteArray, ctxt->bytes, 0 ); From c38e3593fbdf3d5c80405b2df1d232e329118eef Mon Sep 17 00:00:00 2001 From: Andy2 Date: Sun, 21 Aug 2011 11:39:38 -0700 Subject: [PATCH 13/16] replace hard-coded placeholder names for optional top-line of games list item display with real "aa vs. bb vs..." consed up in GameSummary, and set field to singleline so doesn't wrap when names get too long. --- .../XWords4/res/layout/game_list_item.xml | 1 + .../android/XWords4/res/values/strings.xml | 1 + .../eehouse/android/xw4/GameListAdapter.java | 2 +- .../eehouse/android/xw4/jni/GameSummary.java | 38 +++++++++++-------- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/xwords4/android/XWords4/res/layout/game_list_item.xml b/xwords4/android/XWords4/res/layout/game_list_item.xml index 500669081..62555d926 100644 --- a/xwords4/android/XWords4/res/layout/game_list_item.xml +++ b/xwords4/android/XWords4/res/layout/game_list_item.xml @@ -38,6 +38,7 @@ android:layout_height="wrap_content" android:gravity="center" android:layout_weight="1" + android:singleLine="true" android:textAppearance="?android:attr/textAppearanceMedium" /> diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml index 05ad8c121..8cb0be23e 100644 --- a/xwords4/android/XWords4/res/values/strings.xml +++ b/xwords4/android/XWords4/res/values/strings.xml @@ -124,6 +124,7 @@ %s (remote) %s (remote) + \u0020vs.\u0020 %1$s (%2$s) Bonus for using all tiles: 50\n Score for turn: %d\n diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java index a8eb2d1dc..fbf0d3577 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameListAdapter.java @@ -134,7 +134,7 @@ public class GameListAdapter extends XWListAdapter { summary.dictLang ); break; case R.string.game_summary_field_opponents: - value = "vs Kati +2"; + value = summary.playerNames(); break; case R.string.game_summary_field_state: value = state; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java index fc27bf958..51296432a 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java @@ -21,6 +21,7 @@ package org.eehouse.android.xw4.jni; import android.content.Context; +import android.text.TextUtils; import junit.framework.Assert; import org.eehouse.android.xw4.R; import org.eehouse.android.xw4.Utils; @@ -86,22 +87,11 @@ public class GameSummary { if ( null == m_gi ) { result = m_playersSummary; } else { - StringBuffer sb = new StringBuffer(); - for ( int ii = 0; ; ) { - - int score = 0; - try { - // scores can be null, but I've seen array OOB too. - score = scores[ii]; - } catch ( Exception ex ){} - - sb.append( m_gi.players[ii].name ); - if ( ++ii >= nPlayers ) { - break; - } - sb.append( "\n" ); + String[] names = new String[nPlayers]; + for ( int ii = 0; ii < nPlayers; ++ii ) { + names[ii] = m_gi.players[ii].name; } - result = sb.toString(); + result = TextUtils.join( "\n", names ); m_playersSummary = result; } return result; @@ -199,6 +189,24 @@ public class GameSummary { return player; } + public String playerNames() + { + String[] names = null; + if ( null != m_gi ) { + names = m_gi.visibleNames() + } else if ( null != m_playersSummary ) { + names = TextUtils.split( m_playersSummary, "\n" ); + } + + String result = null; + if ( null != names && 0 < names.length ) { + String joiner = m_context.getString( R.string.vs_join ); + result = TextUtils.join( joiner, names ); + } + + return result; + } + public boolean isNextToPlay( int indx ) { return indx == turn && isLocal(indx); } From 56fd01a05b9d66e624eb0cb0d812e5ae3eb9d088 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Sun, 21 Aug 2011 12:07:21 -0700 Subject: [PATCH 14/16] oops -- missing semicolon. --- .../XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java index 51296432a..6a2dee187 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/GameSummary.java @@ -193,7 +193,7 @@ public class GameSummary { { String[] names = null; if ( null != m_gi ) { - names = m_gi.visibleNames() + names = m_gi.visibleNames(); } else if ( null != m_playersSummary ) { names = TextUtils.split( m_playersSummary, "\n" ); } From 5c8afa06ce2bae947435ca849216876f88eec974 Mon Sep 17 00:00:00 2001 From: Andy2 Date: Sun, 21 Aug 2011 12:07:45 -0700 Subject: [PATCH 15/16] version string and changelog for beta 33 --- xwords4/android/XWords4/AndroidManifest.xml | 2 +- xwords4/android/XWords4/res/raw/changes | 32 +++++++++++-------- .../XWords4/res/values/common_rsrc.xml | 2 +- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/xwords4/android/XWords4/AndroidManifest.xml b/xwords4/android/XWords4/AndroidManifest.xml index b3b869dde..3067e89a4 100644 --- a/xwords4/android/XWords4/AndroidManifest.xml +++ b/xwords4/android/XWords4/AndroidManifest.xml @@ -22,7 +22,7 @@ to come from a domain that you own or have control over. --> diff --git a/xwords4/android/XWords4/res/raw/changes b/xwords4/android/XWords4/res/raw/changes index bb4d2bccd..7ef1b14a2 100644 --- a/xwords4/android/XWords4/res/raw/changes +++ b/xwords4/android/XWords4/res/raw/changes @@ -6,27 +6,33 @@ -Crosswords 4.4 beta 32 release - -

32's a quickie to fix a crash introduced in 31. So I'll leave the -older changes in place

+Crosswords 4.4 beta 33 release
    -
  • Give choice between sending invites as TEXT or HTML, then format - differently depending
  • +
  • Use the term "wordlist" instead of "dictionary" to not imply that + there are definitions provided.
  • -
  • include link in invites to install Crosswords
  • +
  • Change how downloaded wordlists are opened to consume much less + Java memory. Downside: wordlists on external storage are not + available when that storage is mounted on a computer over USB.
  • -
  • Add expand/contract buttons to games list
  • +
  • In the Wordlists screen, hide wordlists stored on external media + when the media's not available. Notice when it becomes available + and redraw the list.
  • + +
  • Make a game's language one of several configurable attributes + that can be displayed along with the game
  • + +
  • Fix rename of games to work when keyboard opened while dialog + up.
  • + +
  • Add icons to games list display so it's easier to tell networked + from standalone. These are placeholders until I can get an artist + to help out. :-)
  • -
  • Add ability to rename games
  • - -
  • Don't crash when user chooses "Copy"
  • -
-

Please remember that this is beta software. Please let me know (at eehouse@eehouse.org) what's broken and what features you'd most like to see.

diff --git a/xwords4/android/XWords4/res/values/common_rsrc.xml b/xwords4/android/XWords4/res/values/common_rsrc.xml index 89f7ac356..0b8d1ba2b 100644 --- a/xwords4/android/XWords4/res/values/common_rsrc.xml +++ b/xwords4/android/XWords4/res/values/common_rsrc.xml @@ -78,7 +78,7 @@ http://eehouse.org/and_dicts_hh - 4.4 beta 32 + 4.4 beta 33 //%1$s/redir.php From c0dae019d590a5fba4bd2dd892d7a53994a8443f Mon Sep 17 00:00:00 2001 From: Andy2 Date: Sun, 21 Aug 2011 12:19:58 -0700 Subject: [PATCH 16/16] use same context menu header, including wordlist name, as for game list items. --- .../XWords4/src/org/eehouse/android/xw4/DictsActivity.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java index 3cf294cf9..45aabc836 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/DictsActivity.java @@ -377,6 +377,9 @@ public class DictsActivity extends ExpandableListActivity || ! GameUtils.haveWriteableSD() ) { menu.removeItem( R.id.dicts_item_move ); } + + String fmt = getString(R.string.game_item_menu_titlef ); + menu.setHeaderTitle( String.format( fmt, row.getText() ) ); } }