diff --git a/xwords4/android/XWords4/archive/R.java b/xwords4/android/XWords4/archive/R.java
index 4579b70f2..209638e3c 100644
--- a/xwords4/android/XWords4/archive/R.java
+++ b/xwords4/android/XWords4/archive/R.java
@@ -2435,6 +2435,7 @@ public final class R {
placed, i.e. they do not form a single word.
*/
public static final int str_no_empties_in_turn=0x7f05017d;
+ public static final int str_no_hint_found=0x7f05030a;
/** Same as above, but used when you try to show tiles belonging
to a player on another device (a remote player.)
*/
diff --git a/xwords4/android/XWords4/res/values/strings.xml b/xwords4/android/XWords4/res/values/strings.xml
index dfe48c0a8..0627d365a 100644
--- a/xwords4/android/XWords4/res/values/strings.xml
+++ b/xwords4/android/XWords4/res/values/strings.xml
@@ -2529,4 +2529,6 @@
Use Bluetooth to play against a
nearby device that\'s \"paired\" with yours.
+ Cannot find any moves
+
diff --git a/xwords4/android/XWords4/res_src/values-ba_CK/strings.xml b/xwords4/android/XWords4/res_src/values-ba_CK/strings.xml
index e3b4195f7..4899b352d 100644
--- a/xwords4/android/XWords4/res_src/values-ba_CK/strings.xml
+++ b/xwords4/android/XWords4/res_src/values-ba_CK/strings.xml
@@ -2178,4 +2178,5 @@
i.e. yreve reirrac ni eht dlrow tpecxe Nozirev dna Tnirps.
Esu Htooteulb ot yalp tsniaga a
ybraen ecived taht\'s \"deriap\" htiw sruoy.
+ Tonnac dnif yna sevom
diff --git a/xwords4/android/XWords4/res_src/values-ca_PS/strings.xml b/xwords4/android/XWords4/res_src/values-ca_PS/strings.xml
index ad10613b9..bfb9818ba 100644
--- a/xwords4/android/XWords4/res_src/values-ca_PS/strings.xml
+++ b/xwords4/android/XWords4/res_src/values-ca_PS/strings.xml
@@ -2178,4 +2178,5 @@
I.E. EVERY CARRIER IN THE WORLD EXCEPT VERIZON AND SPRINT.
USE BLUETOOTH TO PLAY AGAINST A
NEARBY DEVICE THAT\'S \"PAIRED\" WITH YOURS.
+ CANNOT FIND ANY MOVES
diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java
index 3bff32314..d11b650d8 100644
--- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java
+++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java
@@ -1719,6 +1719,7 @@ public class BoardDelegate extends DelegateBase
public void userError( int code )
{
int resid = 0;
+ boolean asToast = false;
switch( code ) {
case UtilCtxt.ERR_TILES_NOT_IN_LINE:
resid = R.string.str_tiles_not_in_line;
@@ -1764,10 +1765,23 @@ public class BoardDelegate extends DelegateBase
case ERR_REG_SERVER_SANS_REMOTE:
resid = R.string.str_reg_server_sans_remote;
break;
+ case ERR_NO_HINT_FOUND:
+ resid = R.string.str_no_hint_found;
+ asToast = true;
+ break;
}
if ( resid != 0 ) {
- nonBlockingDialog( DlgID.DLG_OKONLY, getString( resid ) );
+ if ( asToast ) {
+ final int residf = resid;
+ runOnUiThread( new Runnable() {
+ public void run() {
+ showToast( residf );
+ }
+ } );
+ } else {
+ nonBlockingDialog( DlgID.DLG_OKONLY, getString( resid ) );
+ }
}
} // userError
diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java
index 417a3b98a..dd444de14 100644
--- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java
+++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesListDelegate.java
@@ -1598,6 +1598,7 @@ public class GamesListDelegate extends ListDelegateBase
for ( long row : rows ) {
m_selGames.remove( row );
}
+ invalidateOptionsMenuIf();
setTitleBar();
}
}
diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java
index 82c6246f8..d4bf807aa 100644
--- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java
+++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/UtilCtxt.java
@@ -125,7 +125,9 @@ public interface UtilCtxt {
static final int ERR_NO_EMPTY_TRADE = 13;
static final int ERR_CANT_UNDO_TILEASSIGN = 14;
static final int ERR_CANT_HINT_WHILE_DISABLED = 15;
- static final int ERR_RELAY_BASE = 16;
+ static final int ERR_NO_HINT_FOUND = 16;
+
+ static final int ERR_RELAY_BASE = 17;
void userError( int id );
void informMove( String expl, String words );
diff --git a/xwords4/android/scripts/common_targets.xml b/xwords4/android/scripts/common_targets.xml
index 9ca2678c6..ef91f60e6 100644
--- a/xwords4/android/scripts/common_targets.xml
+++ b/xwords4/android/scripts/common_targets.xml
@@ -110,4 +110,16 @@
+
+
+
+
+
+
+
+ Created XWords4-debug-${git-rev}.apk
+
+
+
+
diff --git a/xwords4/android/scripts/info.py b/xwords4/android/scripts/info.py
index 310758fd9..cd435e68f 100755
--- a/xwords4/android/scripts/info.py
+++ b/xwords4/android/scripts/info.py
@@ -1,7 +1,7 @@
#!/usr/bin/python
# Script meant to be installed on eehouse.org.
-import logging, shelve, hashlib, sys, json, subprocess, glob, os, struct, random
+import logging, shelve, hashlib, sys, json, subprocess, glob, os, struct, random, string
import mk_for_download, mygit
import xwconfig
@@ -161,10 +161,13 @@ def getDictSums():
openShelf()
return s_shelf[k_SUMS]
-def getOrderedApks( path ):
+def getOrderedApks( path, debug ):
+ # logging.debug( "getOrderedApks(" + path + ")" )
apks = []
- pattern = path + "/XWords4-release_*android_beta_*.apk"
+ pattern = path
+ if debug: pattern += "/XWords4-debug-android_*.apk"
+ else: pattern += "/XWords4-release_*android_beta_*.apk"
files = ((os.stat(apk).st_mtime, apk) for apk in glob.glob(pattern))
for mtime, file in sorted(files, reverse=True):
@@ -173,6 +176,14 @@ def getOrderedApks( path ):
return apks
+def getVariantDir( name ):
+ result = ''
+ splits = string.split( name, '.' )
+ last = splits[-1]
+ if not last == 'xw4': result = last + '/'
+ # logging.debug( 'getVariantDir(' + name + ") => " + result )
+ return result
+
# public, but deprecated
def curVersion( req, name, avers = 41, gvers = None, installer = None ):
global k_versions
@@ -218,12 +229,22 @@ def getApp( params, name ):
if k_NAME in params:
name = params[k_NAME]
if name:
+ variantDir = getVariantDir( name )
# If we're a dev device, always push the latest
if k_DEBUG in params and params[k_DEBUG]:
- url = k_urlbase + '/' + k_apkDir + 'XWords4-debug.apk'
- result = {k_URL: url}
+ dir = k_filebase + k_apkDir + variantDir
+ apks = getOrderedApks( dir, True )
+ if 0 < len(apks):
+ apk = apks[0]
+ curApk = params[k_GVERS] + '.apk'
+ if curApk in apk:
+ logging.debug( "already have " + curApk )
+ else:
+ url = k_urlbase + '/' + k_apkDir + variantDir + apk[len(dir):]
+ logging.debug("url: " + url)
+ result = {k_URL: url}
elif k_DEVOK in params and params[k_DEVOK]:
- apks = getOrderedApks( k_filebase + k_apkDir )
+ apks = getOrderedApks( k_filebase + k_apkDir, False )
if 0 < len(apks):
apk = apks[0]
# Does path NOT contain name of installed file
@@ -439,7 +460,6 @@ def getUpdates( req, params ):
else:
logging.debug( "NOT FOUND xlate info" )
- logging.debug( 'getUpdates done:', )
result = json.dumps( result )
# logging.debug( result )
return result
@@ -494,7 +514,7 @@ def main():
if argc >= 4: usage()
path = ""
if argc >= 3: path = sys.argv[2]
- apks = getOrderedApks( path )
+ apks = getOrderedApks( path, False )
if 0 == len(apks): print "No apks in", path
for apk in apks:
print apk
diff --git a/xwords4/common/board.c b/xwords4/common/board.c
index 97003fd88..241ae8636 100644
--- a/xwords4/common/board.c
+++ b/xwords4/common/board.c
@@ -2107,6 +2107,7 @@ board_requestHint( BoardCtxt* board,
if ( searchComplete && canMove ) {
model_makeTurnFromMoveInfo( model, selPlayer, &newMove);
} else {
+ result = XP_FALSE;
XP_STATUSF( "unable to complete hint request\n" );
}
*workRemainsP = !searchComplete;
@@ -2122,6 +2123,10 @@ board_requestHint( BoardCtxt* board,
setArrowVisible( board, wasVisible );
}
}
+
+ if ( !result ) {
+ util_userError( board->util, ERR_NO_HINT_FOUND );
+ }
}
return result || redraw;
} /* board_requestHint */
diff --git a/xwords4/common/engine.c b/xwords4/common/engine.c
index ecb7c5830..b1aa40c62 100644
--- a/xwords4/common/engine.c
+++ b/xwords4/common/engine.c
@@ -393,6 +393,7 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
{
XP_Bool result = XP_TRUE;
XP_U16 star_row;
+ XP_Bool canMove = XP_FALSE;
engine->nTilesMax = XP_MIN( MAX_TRAY_TILES, nTiles );
#ifdef XWFEATURE_BONUSALL
@@ -445,9 +446,9 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
dictionary's emtpy or there are no tiles, still return TRUE so we don't
get scheduled again. Fixes infinite loop with empty dict and a
robot. */
- *canMoveP = NULL != dict_getTopEdge(engine->dict)
+ canMove = NULL != dict_getTopEdge(engine->dict)
&& initTray( engine, tiles, nTiles );
- if ( *canMoveP ) {
+ if ( canMove ) {
util_engineStarting( engine->util,
engine->rack[engine->blankTile] );
@@ -528,6 +529,7 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
XP_MEMCPY( newMove, &move->moveInfo, sizeof(*newMove) );
} else {
newMove->nTiles = 0;
+ canMove = XP_FALSE;
}
result = XP_TRUE;
}
@@ -540,6 +542,7 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
newMove->nTiles = 0;
}
+ *canMoveP = canMove;
return result;
} /* engine_findMove */
diff --git a/xwords4/common/util.h b/xwords4/common/util.h
index 06a55f4de..02f093a5f 100644
--- a/xwords4/common/util.h
+++ b/xwords4/common/util.h
@@ -58,6 +58,7 @@ typedef enum {
/* ERR_NOT_YOUR_TURN_TO_MOVE, */
ERR_CANT_UNDO_TILEASSIGN,
ERR_CANT_HINT_WHILE_DISABLED,
+ ERR_NO_HINT_FOUND, /* not really an error... */
ERR_RELAY_BASE,
ERR_RELAY_END = ERR_RELAY_BASE + XWRELAY_ERROR_LASTERR
diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c
index 0f16539ed..2920777a0 100644
--- a/xwords4/linux/linuxutl.c
+++ b/xwords4/linux/linuxutl.c
@@ -492,6 +492,10 @@ linux_getErrString( UtilErrID id, XP_Bool* silent )
message = "No tiles selected; trade cancelled.";
break;
+ case ERR_NO_HINT_FOUND:
+ message = "Unable to suggest any moves.";
+ break;
+
case ERR_CANT_UNDO_TILEASSIGN:
message = "Tile assignment can't be undone.";
break;