Don't accept invitation again for same channel

Do away with debug setting to accept duplicate invitations. Change
definition of duplicate to mean specifying a channel and gameID that
already exist. So now send-to-self works without a debug
preference. Accidentally clicking on the same emailed invite twice will
still be blocked. There will be problems if a game's been deleted but
those have probably always been here.
This commit is contained in:
Eric House 2020-08-29 16:11:24 -07:00
parent 42575f1380
commit a0beecafc3
8 changed files with 61 additions and 103 deletions

View file

@ -826,13 +826,35 @@ public class DBUtils {
} }
cursor.close(); cursor.close();
} }
if ( null != result && 1 < result.length ) { if ( 1 < result.length ) {
Log.i( TAG, "getRowIDsFor(%x)=>length %d array", gameID, Log.i( TAG, "getRowIDsFor(%x)=>length %d array", gameID,
result.length ); result.length );
} }
return result; return result;
} }
static Map<Long, Integer> getRowIDsAndChannels( Context context, int gameID )
{
Map<Long, Integer> result = new HashMap<>();
String[] columns = { ROW_ID, DBHelper.GIFLAGS };
String selection = String.format( DBHelper.GAMEID + "=%d", gameID );
initDB( context );
synchronized( s_dbHelper ) {
Cursor cursor = query( TABLE_NAMES.SUM, columns, selection );
while ( cursor.moveToNext() ) {
int flags = cursor.getInt( cursor.getColumnIndex( DBHelper.GIFLAGS ) );
int forceChannel = (flags >> GameSummary.FORCE_CHANNEL_OFFSET)
& GameSummary.FORCE_CHANNEL_MASK;
long rowid = cursor.getLong( cursor.getColumnIndex( ROW_ID ) );
result.put( rowid, forceChannel );
// Log.i( TAG, "getRowIDsAndChannels(): added %d => %d",
// rowid, forceChannel );
}
cursor.close();
}
return result;
}
public static boolean haveGame( Context context, int gameID ) public static boolean haveGame( Context context, int gameID )
{ {
long[] rows = getRowIDsFor( context, gameID ); long[] rows = getRowIDsFor( context, gameID );
@ -900,28 +922,6 @@ public class DBUtils {
} }
} }
// Return creation time of newest game matching this nli, or null
// if none found.
public static Date getMostRecentCreate( Context context, int gameID )
{
Date result = null;
String selection = String.format("%s=%d", DBHelper.GAMEID, gameID );
String[] columns = { DBHelper.CREATE_TIME };
initDB( context );
synchronized( s_dbHelper ) {
Cursor cursor = query( TABLE_NAMES.SUM, columns, selection );
if ( cursor.moveToNext() ) {
int indx = cursor.getColumnIndex( columns[0] );
result = new Date( cursor.getLong( indx ) );
}
cursor.close();
}
return result;
}
public static String[] getRelayIDs( Context context, long[][] rowIDs ) public static String[] getRelayIDs( Context context, long[][] rowIDs )
{ {
String[] result = null; String[] result = null;

View file

@ -2341,26 +2341,30 @@ public class GamesListDelegate extends ListDelegateBase
boolean handled = false; boolean handled = false;
Assert.assertTrue( nli.isValid() ); Assert.assertTrue( nli.isValid() );
Date create = null; Map<Long, Integer> rowids = DBUtils
create = DBUtils.getMostRecentCreate( m_activity, nli.gameID() ); .getRowIDsAndChannels( m_activity, nli.gameID() );
if ( null == create ) { if ( 0 < rowids.size() ) {
if ( checkWarnNoDict( nli ) ) { // There's already a game? Better not have same channel as invite
makeNewNetGame( nli ); // creates
handled = true; for ( long rowid : rowids.keySet() ) {
if ( nli.forceChannel == rowids.get(rowid) ) {
// May not want this at all if it's never a bad thing
makeOkOnlyBuilder( R.string.dropped_dupe )
.setParams(rowid)
.setTitle("add open game button")
.show();
handled = true;
break;
}
} }
} else if ( XWPrefs.getSecondInviteAllowed( m_activity ) ) { }
String msg = getString( R.string.dup_game_query_fmt,
create.toString() ); if ( !handled && checkWarnNoDict( nli ) ) {
m_netLaunchInfo = nli; makeNewNetGame( nli );
makeConfirmThenBuilder( msg, Action.NEW_NET_GAME )
.setParams( nli )
.show();
handled = true;
} else {
makeOkOnlyBuilder( R.string.dropped_dupe ).show();
handled = true; handled = true;
} }
return handled; return handled;
} // startNewNetGame } // startNewNetGame

View file

@ -57,11 +57,6 @@ public class XWPrefs {
BuildConfig.DEBUG ); BuildConfig.DEBUG );
} }
public static boolean getSecondInviteAllowed( Context context )
{
return getPrefsBoolean( context, R.string.key_enable_dup_invite, false );
}
public static boolean moveCountEnabled( Context context ) public static boolean moveCountEnabled( Context context )
{ {
return getPrefsBoolean( context, R.string.key_enable_pending_count, return getPrefsBoolean( context, R.string.key_enable_pending_count,

View file

@ -135,48 +135,18 @@ abstract class XWServiceHelper {
} else { } else {
success = true; success = true;
} }
CurGameInfo gi = null;
if ( success ) { if ( success ) {
long[] rowids = DBUtils.getRowIDsFor( mContext, nli.gameID() ); Map<Long, Integer> rowids = DBUtils.getRowIDsAndChannels( mContext, nli.gameID() );
if ( 0 == rowids.length ) { // Accept only if there isn't already a game with the channel
// cool: we're good for ( long rowid : rowids.keySet() ) {
} else if ( rowids.length < nli.nPlayersT ) { if ( rowids.get( rowid ) == nli.forceChannel ) {
success = XWPrefs.getSecondInviteAllowed( mContext ); if ( BuildConfig.DEBUG ) {
DbgUtils.showf( mContext, "Dropping duplicate invite" );
if ( BuildConfig.DEBUG && !success ) {
DbgUtils.showf( mContext, "Dropping duplicate invite" );
}
// Allowing a second game allows the common testing action of
// sending invitation to myself. But we still need to check
// for duplicates! forceChannel's hard to dig up, but works
for ( int ii = 0; success && ii < rowids.length; ++ii ) {
long rowid = rowids[ii];
try ( GameLock lock = GameLock.tryLockRO( rowid ) ) {
// drop invite if can't open game; likely a dupe!
if ( null != lock ) {
gi = new CurGameInfo( mContext );
GamePtr gamePtr = GameUtils
.loadMakeGame( mContext, gi, lock );
gamePtr.release();
} else {
DbgUtils.toastNoLock( TAG, mContext, rowid,
"handleInvitation()" );
}
} }
success = false;
if ( null == gi ) { break;
// locked. Maybe it's open?
try ( JNIThread thrd = JNIThread.getRetained( rowid ) ) {
if ( null != thrd ) {
gi = thrd.getGI();
}
}
}
success = null != gi && gi.forceChannel != nli.forceChannel;
} }
} else {
success = false;
} }
if ( success ) { if ( success ) {
@ -198,7 +168,7 @@ abstract class XWServiceHelper {
} }
} }
} }
Log.d( TAG, "handleInvitation() => %b (gi: %s)", success, gi ); Log.d( TAG, "handleInvitation() => %b", success );
return success; return success;
} }

View file

@ -58,6 +58,8 @@ public class GameSummary implements Serializable {
public static final int MSG_FLAGS_GAMEOVER = 4; public static final int MSG_FLAGS_GAMEOVER = 4;
public static final int MSG_FLAGS_ALL = 7; public static final int MSG_FLAGS_ALL = 7;
public static final int DUP_MODE_MASK = 1 << (CurGameInfo.MAX_NUM_PLAYERS * 2); public static final int DUP_MODE_MASK = 1 << (CurGameInfo.MAX_NUM_PLAYERS * 2);
public static final int FORCE_CHANNEL_OFFSET = (CurGameInfo.MAX_NUM_PLAYERS * 2) + 1;
public static final int FORCE_CHANNEL_MASK = 0x03;
public int lastMoveTime; // set by jni's server.c on move receipt public int lastMoveTime; // set by jni's server.c on move receipt
public int dupTimerExpires; public int dupTimerExpires;
@ -372,6 +374,12 @@ public class GameSummary implements Serializable {
if ( m_gi.inDuplicateMode ) { if ( m_gi.inDuplicateMode ) {
result |= DUP_MODE_MASK; result |= DUP_MODE_MASK;
} }
Assert.assertTrue( (result & (FORCE_CHANNEL_MASK<<FORCE_CHANNEL_OFFSET)) == 0 );
// Make sure it's big enough
Assert.assertTrue( 0 == (~FORCE_CHANNEL_MASK & m_gi.forceChannel) );
result |= m_gi.forceChannel << FORCE_CHANNEL_OFFSET;
Log.d( TAG, "giflags(): adding forceChannel %d", m_gi.forceChannel );
} }
return result; return result;
} }

View file

@ -133,7 +133,6 @@
<string name="key_na_sms_invite_flakey">key_na_sms_invite_flakey</string> <string name="key_na_sms_invite_flakey">key_na_sms_invite_flakey</string>
<string name="key_na_dicts">key_na_dicts</string> <string name="key_na_dicts">key_na_dicts</string>
<string name="key_enable_debug">key_enable_debug</string> <string name="key_enable_debug">key_enable_debug</string>
<string name="key_enable_dup_invite">key_enable_dup_invite</string>
<string name="key_enable_pending_count">key_enable_pending_count</string> <string name="key_enable_pending_count">key_enable_pending_count</string>
<string name="key_enable_sms_toself">key_enable_sms_toself</string> <string name="key_enable_sms_toself">key_enable_sms_toself</string>
<string name="key_show_fcm">key_show_fcm2</string> <string name="key_show_fcm">key_show_fcm2</string>

View file

@ -1268,16 +1268,6 @@
connect to the relay failed because the room named does not connect to the relay failed because the room named does not
exist. (I believe this no longer occurs.) --> exist. (I believe this no longer occurs.) -->
<string name="button_retry">Retry</string> <string name="button_retry">Retry</string>
<!-- Shown in the main screen when you launch CrossWords from an
invitation (received in email or messaging app, say) and
there's already a game running that matches that invitation.
It's to prevent you from opening multiple games and getting
confused. But some people who play together all the time use
the same room name over and over so they'll get this warning
and it's harmless to ignore it. -->
<string name="dup_game_query_fmt">You already have a game that seems
to have been created (on %1$s) from the same invitation. Are you
sure you want to create another?</string>
<!-- Title of generic dialog used to display information --> <!-- Title of generic dialog used to display information -->
<string name="info_title">FYI…</string> <string name="info_title">FYI…</string>
<!-- title of dialog allowing user to pick tiles "face up". (This <!-- title of dialog allowing user to pick tiles "face up". (This
@ -2277,9 +2267,7 @@
<string name="gamel_menu_storedb">Write games to SD card</string> <string name="gamel_menu_storedb">Write games to SD card</string>
<string name="gamel_menu_loaddb">Load games from SD card</string> <string name="gamel_menu_loaddb">Load games from SD card</string>
<string name="gamel_menu_writegit">Copy git info to clipboard</string> <string name="gamel_menu_writegit">Copy git info to clipboard</string>
<string name="enable_dupes_title">Accept duplicate invites</string>
<string name="xlations_locale">Fake locale for translation</string> <string name="xlations_locale">Fake locale for translation</string>
<string name="enable_dupes_summary">Accept invitations more than once</string>
<string name="enable_pending_count_title">Show Pending messages</string> <string name="enable_pending_count_title">Show Pending messages</string>
<string name="enable_pending_count_summary">Show number not yet acknowledged</string> <string name="enable_pending_count_summary">Show number not yet acknowledged</string>
<string name="nag_intervals">Reminder intervals (minutes1,minutes2,…)</string> <string name="nag_intervals">Reminder intervals (minutes1,minutes2,…)</string>

View file

@ -392,12 +392,6 @@
android:title="@string/nag_intervals" android:title="@string/nag_intervals"
/> />
<CheckBoxPreference android:key="@string/key_enable_dup_invite"
android:title="@string/enable_dupes_title"
android:summary="@string/enable_dupes_summary"
android:defaultValue="@bool/DEBUG"
/>
<CheckBoxPreference android:key="@string/key_enable_pending_count" <CheckBoxPreference android:key="@string/key_enable_pending_count"
android:title="@string/enable_pending_count_title" android:title="@string/enable_pending_count_title"
android:summary="@string/enable_pending_count_summary" android:summary="@string/enable_pending_count_summary"