diff --git a/xwords4/android/XWords4/archive/R.java b/xwords4/android/XWords4/archive/R.java index 0a1994f2b..24ac6854f 100644 --- a/xwords4/android/XWords4/archive/R.java +++ b/xwords4/android/XWords4/archive/R.java @@ -1603,19 +1603,22 @@ public final class R { /** Text of progress indicator shown while check is being conducted */ public static final int msgs_progress=0x7f05009f; - /** body of warning notification reminder message - */ - public static final int nag_body_fmt=0x7f0502c8; - public static final int nag_days_fmt=0x7f0502cd; - public static final int nag_hours_fmt=0x7f0502cc; + public static final int nag_body_fmt=0x7f0502cb; + public static final int nag_days_fmt=0x7f0502ca; + public static final int nag_hours_fmt=0x7f0502c9; public static final int nag_intervals=0x7f05008d; - public static final int nag_minutes_fmt=0x7f0502cb; + /** body of warning notification reminder message. First three + are used to build a string based on the length of time that's then + inserted in the fourth. E.g "PlayerName moved more than 2 day[s], + 4 hour[s] ago." + */ + public static final int nag_minutes_fmt=0x7f0502c8; /** Nagging: title of notification reminder message */ public static final int nag_title=0x7f0502c7; /** above is inserted in this the last time I warn */ - public static final int nag_warn_last_fmt=0x7f0502c9; + public static final int nag_warn_last_fmt=0x7f0502cc; public static final int name_dict_fmt=0x7f050087; /** text of checkbox. If this checkbox is checked, games created for network play will by default have the hint feature @@ -2092,7 +2095,7 @@ public final class R { public static final int prefs_names_summary=0x7f050134; /** Used when prev player's name can't be looked up */ - public static final int prev_player=0x7f0502ca; + public static final int prev_player=0x7f0502cd; public static final int progress_title=0x7f0502be; /** */ diff --git a/xwords4/android/XWords4/jni/xwjni.c b/xwords4/android/XWords4/jni/xwjni.c index 46c2da455..4821d5a64 100644 --- a/xwords4/android/XWords4/jni/xwjni.c +++ b/xwords4/android/XWords4/jni/xwjni.c @@ -202,6 +202,16 @@ setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi ) } } /* setJGI */ +static void +setLMI( JNIEnv* env, jobject jlmi, const LastMoveInfo* lmi ) +{ + setInt( env, jlmi, "score", lmi->score ); + setInt( env, jlmi, "nTiles", lmi->nTiles ); + setInt( env, jlmi, "moveType", lmi->moveType ); + setString( env, jlmi, "name", lmi->name ); + setString( env, jlmi, "word", lmi->word ); +} + #ifdef COMMON_LAYOUT static const SetInfo bd_ints[] = { ARR_MEMBER( BoardDims, left ) @@ -1135,17 +1145,19 @@ Java_org_eehouse_android_xw4_jni_XwJNI_model_1getNumTilesInTray JNIEXPORT jstring JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_model_1getPlayersLastScore -(JNIEnv* env, jclass C, jint gamePtr, jint player ) +( JNIEnv* env, jclass C, jint gamePtr, jint player, jobject jlmi ) { jstring result = NULL; XWJNI_START(); XP_ASSERT( !!state->game.model ); XP_UCHAR buf[64] = {0}; XP_U16 buflen = sizeof(buf); - if ( !model_getPlayersLastScore( state->game.model, player, buf, - &buflen ) ) { + LastMoveInfo lmi; + if ( !model_getPlayersLastScore( state->game.model, player, &lmi, + buf, &buflen ) ) { buf[0] = '\0'; } + setLMI( env, jlmi, &lmi ); result = (*env)->NewStringUTF( env, buf ); XWJNI_END(); return result; diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java index 48aba6952..8b316a8a5 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BTService.java @@ -488,7 +488,7 @@ public class BTService extends XWService { } else if ( haveGame && GameUtils.feedMessage( BTService.this, rowid, buffer, addr, - m_btMsgSink ) ) { + m_btMsgSink, null ) ) { postNotification( gameID, R.string.new_btmove_title, R.string.new_move_body, rowid ); // do nothing 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 29a5acec2..411422e61 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/BoardDelegate.java @@ -1415,8 +1415,10 @@ public class BoardDelegate extends DelegateBase @Override public void playerScoreHeld( int player ) { + LastMoveInfo lmi = new LastMoveInfo(); String expl = XwJNI.model_getPlayersLastScore( m_jniGamePtr, - player ); + player, lmi ); + expl = lmi.format( m_activity ); if ( expl.length() == 0 ) { expl = getString( R.string.no_moves_made ); } 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 a4c7c011f..682f2f4a9 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/GameUtils.java @@ -47,6 +47,7 @@ import org.eehouse.android.xw4.jni.*; import org.eehouse.android.xw4.loc.LocUtils; import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType; import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole; +import org.eehouse.android.xw4.jni.LastMoveInfo; public class GameUtils { @@ -765,7 +766,7 @@ public class GameUtils { public static boolean feedMessages( Context context, long rowid, byte[][] msgs, CommsAddrRec ret, - MultiMsgSink sink ) + MultiMsgSink sink, LastMoveInfo lmi ) { boolean draw = false; Assert.assertTrue( -1 != rowid ); @@ -797,6 +798,10 @@ public class GameUtils { DBUtils.saveThumbnail( context, lock, bitmap ); } + if ( null != lmi ) { + XwJNI.model_getPlayersLastScore( gamePtr, 0, lmi ); + } + saveGame( context, gamePtr, gi, lock, false ); summarizeAndClose( context, lock, gamePtr, gi, feedImpl ); @@ -813,12 +818,13 @@ public class GameUtils { } // feedMessages public static boolean feedMessage( Context context, long rowid, byte[] msg, - CommsAddrRec ret, MultiMsgSink sink ) + CommsAddrRec ret, MultiMsgSink sink, + LastMoveInfo lmi ) { Assert.assertTrue( DBUtils.ROWID_NOTFOUND != rowid ); byte[][] msgs = new byte[1][]; msgs[0] = msg; - return feedMessages( context, rowid, msgs, ret, sink ); + return feedMessages( context, rowid, msgs, ret, sink, lmi ); } // This *must* involve a reset if the language is changing!!! diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java index 0b934e170..d6962a247 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java @@ -51,6 +51,7 @@ import org.eehouse.android.xw4.jni.GameSummary; import org.eehouse.android.xw4.jni.UtilCtxt; import org.eehouse.android.xw4.jni.UtilCtxt.DevIDType; import org.eehouse.android.xw4.jni.XwJNI; +import org.eehouse.android.xw4.jni.LastMoveInfo; import org.eehouse.android.xw4.loc.LocUtils; public class RelayService extends XWService @@ -365,23 +366,28 @@ public class RelayService extends XWService startService( this ); // bad name: will *stop* threads too } - private void setupNotification( String[] relayIDs ) + private void setupNotifications( String[] relayIDs ) { for ( String relayID : relayIDs ) { long[] rowids = DBUtils.getRowIDsFor( this, relayID ); if ( null != rowids ) { for ( long rowid : rowids ) { - setupNotification( rowid ); + setupNotification( rowid, null ); } } } } - private void setupNotification( long rowid ) + private void setupNotification( long rowid, LastMoveInfo lmi ) { Intent intent = GamesListDelegate.makeRowidIntent( this, rowid ); - String msg = LocUtils.getString( this, R.string.notify_body_fmt, - GameUtils.getName( this, rowid ) ); + String msg; + if ( null == lmi ) { + msg = LocUtils.getString( this, R.string.notify_body_fmt, + GameUtils.getName( this, rowid ) ); + } else { + msg = lmi.format( this ); + } Utils.postNotification( this, intent, R.string.notify_title, msg, (int)rowid ); } @@ -861,9 +867,10 @@ public class RelayService extends XWService } else { RelayMsgSink sink = new RelayMsgSink(); sink.setRowID( rowid ); + LastMoveInfo lmi = new LastMoveInfo(); if ( GameUtils.feedMessage( this, rowid, msg, null, - sink ) ) { - setupNotification( rowid ); + sink, lmi ) ) { + setupNotification( rowid, lmi ); } else { DbgUtils.logf( "feedMessage(): background dropped it" ); } @@ -898,7 +905,7 @@ public class RelayService extends XWService if ( BoardDelegate.feedMessages( rowIDs[ii], forOne ) || GameUtils.feedMessages( this, rowIDs[ii], forOne, null, - sink ) ) { + sink, null ) ) { idsWMsgs.add( relayIDs[ii] ); } else { DbgUtils.logf( "message for %s (rowid %d) not consumed", @@ -909,7 +916,7 @@ public class RelayService extends XWService if ( 0 < idsWMsgs.size() ) { String[] tmp = new String[idsWMsgs.size()]; idsWMsgs.toArray( tmp ); - setupNotification( tmp ); + setupNotifications( tmp ); } sink.send( this ); } diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java index 7903ba3b9..f66d748ea 100644 --- a/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/SMSService.java @@ -776,7 +776,7 @@ public class SMSService extends XWService { } else { SMSMsgSink sink = new SMSMsgSink( this ); if ( GameUtils.feedMessage( this, rowid, msg, addr, - sink ) ) { + sink, null ) ) { postNotification( gameID, R.string.new_smsmove_title, getString(R.string.new_move_body), rowid ); diff --git a/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/LastMoveInfo.java b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/LastMoveInfo.java new file mode 100644 index 000000000..8299da5c2 --- /dev/null +++ b/xwords4/android/XWords4/src/org/eehouse/android/xw4/jni/LastMoveInfo.java @@ -0,0 +1,35 @@ +/* -*- compile-command: "find-and-ant.sh debug install"; -*- */ +/* + * Copyright 2014 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; + +import android.content.Context; + +public class LastMoveInfo { + public String name; + public int moveType; + public int score; + public int nTiles; + public String word; + + public String format( Context context ) + { + return String.format( "%s did move type %d", name, moveType ); + } +} 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 37a5d1e01..0f9e0682b 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 @@ -271,7 +271,8 @@ public class XwJNI { public static native int model_getNMoves( int gamePtr ); public static native int model_getNumTilesInTray( int gamePtr, int player ); public static native String model_getPlayersLastScore( int gamePtr, - int player ); + int player, + LastMoveInfo lmi ); // Server public static native void server_reset( int gamePtr ); public static native void server_handleUndo( int gamePtr ); diff --git a/xwords4/common/draw.h b/xwords4/common/draw.h index 7643d3f47..d542c6f55 100644 --- a/xwords4/common/draw.h +++ b/xwords4/common/draw.h @@ -29,6 +29,7 @@ typedef XP_Bool (*LastScoreCallback)( void* closure, XP_S16 player, + LastMoveInfo* lmi, XP_UCHAR* expl, XP_U16* explLen ); typedef enum { diff --git a/xwords4/common/model.c b/xwords4/common/model.c index 350b14ed2..605d1e658 100644 --- a/xwords4/common/model.c +++ b/xwords4/common/model.c @@ -2189,9 +2189,9 @@ getFirstWord( const XP_UCHAR* word, XP_Bool XP_UNUSED(isLegal), static void scoreLastMove( ModelCtxt* model, MoveInfo* moveInfo, XP_U16 howMany, - XP_UCHAR* buf, XP_U16* bufLen ) + LastMoveInfo* lmi, XP_UCHAR* buf, XP_U16* bufLen ) { - + lmi->nTiles = moveInfo->nTiles; if ( moveInfo->nTiles == 0 ) { const XP_UCHAR* str = util_getUserString( model->vol.util, STR_PASSED ); XP_U16 len = XP_STRLEN( str ); @@ -2224,6 +2224,8 @@ scoreLastMove( ModelCtxt* model, MoveInfo* moveInfo, XP_U16 howMany, format = util_getUserString( model->vol.util, STRSD_SUMMARYSCORED ); *bufLen = XP_SNPRINTF( buf, *bufLen, format, data.word, score ); + lmi->score = score; + XP_SNPRINTF( lmi->word, VSIZE(lmi->word), "%s", data.word ); } } /* scoreLastMove */ @@ -2374,12 +2376,14 @@ model_listWordsThrough( ModelCtxt* model, XP_U16 col, XP_U16 row, XP_Bool model_getPlayersLastScore( ModelCtxt* model, XP_S16 player, + LastMoveInfo* lmi, XP_UCHAR* expl, XP_U16* explLen ) { StackCtxt* stack = model->vol.stack; XP_S16 nEntries, which; StackEntry entry; XP_Bool found = XP_FALSE; + XP_MEMSET( lmi, 0, sizeof(*lmi) ); XP_ASSERT( !!stack ); XP_ASSERT( player >= 0 ); @@ -2398,13 +2402,16 @@ model_getPlayersLastScore( ModelCtxt* model, XP_S16 player, if ( found ) { /* success? */ const XP_UCHAR* format; XP_U16 nTiles; + lmi->name = model->vol.gi->players[player].name; + lmi->moveType = entry.moveType; switch ( entry.moveType ) { case MOVE_TYPE: scoreLastMove( model, &entry.u.move.moveInfo, - nEntries - which, expl, explLen ); + nEntries - which, lmi, expl, explLen ); break; case TRADE_TYPE: nTiles = entry.u.trade.oldTiles.nTiles; + lmi->nTiles = entry.u.trade.oldTiles.nTiles; format = util_getUserString( model->vol.util, STRD_TRADED ); *explLen = XP_SNPRINTF( expl, *explLen, format, nTiles ); break; diff --git a/xwords4/common/model.h b/xwords4/common/model.h index 7df036386..79461291c 100644 --- a/xwords4/common/model.h +++ b/xwords4/common/model.h @@ -76,6 +76,14 @@ typedef struct MoveInfo { MoveInfoTile tiles[MAX_TRAY_TILES]; } MoveInfo; +typedef struct _LastMoveInfo { + const XP_UCHAR* name; + XP_U8 moveType; + XP_U16 score; + XP_U16 nTiles; + XP_UCHAR word[MAX_COLS+1]; +} LastMoveInfo; + typedef XP_U8 TrayTile; typedef struct TrayTileSet { XP_U8 nTiles; @@ -264,6 +272,7 @@ XP_Bool getCurrentMoveScoreIfLegal( ModelCtxt* model, XP_S16 turn, XP_S16 model_getPlayerScore( ModelCtxt* model, XP_S16 player ); XP_Bool model_getPlayersLastScore( ModelCtxt* model, XP_S16 player, + LastMoveInfo* info, XP_UCHAR* expl, XP_U16* explLen ); #ifdef XWFEATURE_BOARDWORDS void model_listWordsThrough( ModelCtxt* model, XP_U16 col, XP_U16 row, diff --git a/xwords4/common/scorebdp.c b/xwords4/common/scorebdp.c index 4e3aca1bc..5a759459f 100644 --- a/xwords4/common/scorebdp.c +++ b/xwords4/common/scorebdp.c @@ -29,12 +29,13 @@ extern "C" { #endif static XP_Bool -board_ScoreCallback( void* closure, XP_S16 player, XP_UCHAR* expl, - XP_U16* explLen) +board_ScoreCallback( void* closure, XP_S16 player, + LastMoveInfo* lmi, + XP_UCHAR* expl, XP_U16* explLen) { ModelCtxt* model = (ModelCtxt*)closure; return model_getPlayersLastScore( model, player, - expl, explLen ); + lmi, expl, explLen ); } /* board_ScoreCallback */ #ifdef XWFEATURE_SCOREONEPASS diff --git a/xwords4/linux/gtkboard.c b/xwords4/linux/gtkboard.c index 610e8fbd2..17c91b1b9 100644 --- a/xwords4/linux/gtkboard.c +++ b/xwords4/linux/gtkboard.c @@ -53,6 +53,7 @@ #include "draw.h" #include "game.h" +#include "movestak.h" #include "gtkask.h" #include "gtkaskm.h" #include "gtkchat.h" @@ -2014,11 +2015,13 @@ gtk_util_playerScoreHeld( XW_UtilCtxt* uc, XP_U16 player ) GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure; - XP_UCHAR scoreExpl[48] = {0}; + XP_UCHAR scoreExpl[128] = {0}; XP_U16 explLen = sizeof(scoreExpl); + LastMoveInfo lmi; if ( model_getPlayersLastScore( globals->cGlobals.game.model, - player, scoreExpl, &explLen ) ) { + player, &lmi, scoreExpl, &explLen ) ) { + formatLMI( &lmi, scoreExpl, VSIZE(scoreExpl) ); (void)gtkask( globals->window, scoreExpl, GTK_BUTTONS_OK, NULL ); } } diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c index 2c0a950a1..547f6739e 100644 --- a/xwords4/linux/linuxutl.c +++ b/xwords4/linux/linuxutl.c @@ -29,6 +29,7 @@ # include /* for backtrace */ #endif +#include "movestak.h" #include "linuxutl.h" #include "main.h" #include "linuxdict.h" @@ -570,6 +571,32 @@ linux_getErrString( UtilErrID id, XP_Bool* silent ) return (XP_UCHAR*)message; } /* linux_getErrString */ +void +formatLMI( const LastMoveInfo* lmi, XP_UCHAR* buf, XP_U16 len ) +{ + const XP_UCHAR* name = lmi->name; + switch( lmi->moveType ) { + case MOVE_TYPE: + if ( 0 == lmi->nTiles ) { + XP_SNPRINTF( buf, len, "%s passed", name ); + } else { + XP_SNPRINTF( buf, len, "%s played %s for %d points", name, + lmi->word, lmi->score ); + } + break; + case TRADE_TYPE: + XP_SNPRINTF( buf, len, "%s traded %d tiles", + name, lmi->nTiles ); + break; + case PHONY_TYPE: + XP_SNPRINTF( buf, len, "%s lost a turn", name ); + break; + default: + XP_ASSERT(0); + break; + } +} + void formatConfirmTrade( const XP_UCHAR** tiles, XP_U16 nTiles, char* buf, XP_U16 buflen ) diff --git a/xwords4/linux/linuxutl.h b/xwords4/linux/linuxutl.h index 91ea0ed42..cd4884f08 100644 --- a/xwords4/linux/linuxutl.h +++ b/xwords4/linux/linuxutl.h @@ -24,6 +24,7 @@ #include "xptypes.h" #include "dictnry.h" +#include "model.h" #include "util.h" #include "main.h" @@ -40,6 +41,7 @@ const XP_UCHAR* linux_getErrString( UtilErrID id, XP_Bool* silent ); void formatConfirmTrade( const XP_UCHAR** tiles, XP_U16 nTiles, char* buf, XP_U16 buflen ); +void formatLMI( const LastMoveInfo* lmi, XP_UCHAR* buf, XP_U16 len ); void initNoConnStorage( CommonGlobals* cGlobals ); XP_Bool storeNoConnMsg( CommonGlobals* cGlobals, const XP_U8* msg, XP_U16 len,