rough implementation of creating and inviting a known player

Followed the way rematch works. Which is gross. Eventually the two
paths (invitee and rematch) should be unified with rematch getting a lot
simpler.)
This commit is contained in:
Eric House 2020-09-21 20:27:03 -07:00
parent b8c588e02c
commit fc05612b74
9 changed files with 315 additions and 48 deletions

View file

@ -530,6 +530,8 @@ public class BoardDelegate extends DelegateBase
Assert.assertTrue( 0 < state.nMissing );
if ( state.summary.hasRematchInfo() ) {
tryRematchInvites( true );
} else if ( state.summary.hasInviteInfo() ) {
tryOtherInvites( true );
} else {
callInviteChoices( sentInfo[0] );
}
@ -1770,6 +1772,8 @@ public class BoardDelegate extends DelegateBase
} else if ( nMissing > 0 ) {
if ( m_summary.hasRematchInfo() ) {
skipDismiss = !tryRematchInvites( false );
} else if ( m_summary.hasInviteInfo() ) {
skipDismiss = !tryOtherInvites( false );
} else if ( !m_haveInvited ) {
m_haveInvited = true;
showInviteAlertIf();
@ -2770,6 +2774,8 @@ public class BoardDelegate extends DelegateBase
{
if ( 0 < m_mySIS.nMissing && m_summary.hasRematchInfo() ) {
tryRematchInvites( false );
} else if ( 0 < m_mySIS.nMissing && m_summary.hasInviteInfo() ) {
tryOtherInvites( false );
} else if ( null != m_missingDevs ) {
Assert.assertNotNull( m_missingMeans );
String gameName = GameUtils.getName( m_activity, m_rowid );
@ -3136,6 +3142,15 @@ public class BoardDelegate extends DelegateBase
}
}
private NetLaunchInfo nliForMe()
{
int numHere = 1;
int forceChannel = 1;
NetLaunchInfo nli = new NetLaunchInfo( m_activity, m_summary, m_gi,
numHere, forceChannel );
return nli;
}
// Return true if anything sent
private boolean tryRematchInvites( boolean force )
{
@ -3148,10 +3163,7 @@ public class BoardDelegate extends DelegateBase
Assert.assertNotNull( m_summary );
Assert.assertNotNull( m_gi );
// only supports a single invite for now!
int numHere = 1;
int forceChannel = 1;
NetLaunchInfo nli = new NetLaunchInfo( m_activity, m_summary, m_gi,
numHere, forceChannel );
NetLaunchInfo nli = nliForMe();
String value;
value = m_summary.getStringExtra( GameSummary.EXTRA_REMATCH_PHONE );
@ -3184,6 +3196,47 @@ public class BoardDelegate extends DelegateBase
return force;
}
private boolean tryOtherInvites( boolean force )
{
boolean result = true;
Assert.assertNotNull( m_summary );
Assert.assertNotNull( m_gi );
String str64 = m_summary.getStringExtra( GameSummary.EXTRA_REMATCH_ADDR );
try {
CommsAddrRec addr = (CommsAddrRec)Utils.string64ToSerializable( str64 );
NetLaunchInfo nli = nliForMe();
CommsConnTypeSet conTypes = addr.conTypes;
for ( CommsConnType typ : conTypes ) {
switch ( typ ) {
case COMMS_CONN_MQTT:
MQTTUtils.inviteRemote( m_activity, addr.mqtt_devID, nli );
recordInviteSent( InviteMeans.MQTT, addr.mqtt_devID );
break;
case COMMS_CONN_BT:
BTService.inviteRemote( m_activity, addr.bt_btAddr, nli );
recordInviteSent( InviteMeans.BLUETOOTH, addr.bt_btAddr );
break;
// case COMMS_CONN_RELAY:
// RelayService.inviteRemote( m_activity, m_jniGamePtr, 0, value, nli );
// recordInviteSent( InviteMeans.RELAY );
// break;
case COMMS_CONN_SMS:
sendNBSInviteIf( addr.sms_phone, nli, true );
recordInviteSent( InviteMeans.SMS_DATA, addr.sms_phone );
break;
default:
Log.d( TAG, "not inviting using addr type %s", typ );
}
}
} catch ( Exception ex ) {
Log.ex( TAG, ex );
Assert.failDbg();
result = false;
}
return result;
}
private void sendNBSInviteIf( String phone, NetLaunchInfo nli,
boolean askOk )
{

View file

@ -38,6 +38,7 @@ import android.text.TextUtils;
import org.eehouse.android.xw4.DBHelper.TABLE_NAMES;
import org.eehouse.android.xw4.DictUtils.DictLoc;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify.InviteMeans;
import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
import org.eehouse.android.xw4.jni.CurGameInfo;
@ -369,6 +370,22 @@ public class DBUtils {
}
} // saveSummary
public static void addRematchInfo( Context context, long rowid, CommsAddrRec addr )
{
try ( GameLock lock = GameLock.tryLock(rowid) ) {
if ( null != lock ) {
String as64 = Utils.serializableToString64( addr );
GameSummary summary = getSummary( context, lock )
.putStringExtra( GameSummary.EXTRA_REMATCH_ADDR, as64 )
;
saveSummary( context, lock, summary );
} else {
Assert.failDbg();
Log.e( TAG, "addRematchInfo(%d): unable to lock game" );
}
}
}
public static void addRematchInfo( Context context, long rowid, String btAddr,
String phone, String relayID, String p2pAddr,
String mqttDevID )

View file

@ -62,6 +62,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.CurGameInfo;
import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.loc.LocUtils;
import java.io.File;
@ -89,6 +90,7 @@ public class GamesListDelegate extends ListDelegateBase
private static final String RELAYIDS_EXTRA = "relayids";
private static final String ROWID_EXTRA = "rowid";
private static final String GAMEID_EXTRA = "gameid";
private static final String INVITEE_REC_EXTRA = "invitee_rec";
private static final String REMATCH_ROWID_EXTRA = "rm_rowid";
private static final String REMATCH_GROUPID_EXTRA = "rm_groupid";
private static final String REMATCH_DICT_EXTRA = "rm_dict";
@ -864,49 +866,14 @@ public class GamesListDelegate extends ListDelegateBase
}
break;
case GAMES_LIST_NEWGAME: {
case GAMES_LIST_NEWGAME:
boolean solo = (Boolean)params[0];
final LinearLayout view = (LinearLayout)
LocUtils.inflate( m_activity, R.layout.msg_label_and_edit );
final EditWClear edit = (EditWClear)view.findViewById( R.id.edit );
edit.setText( GameUtils.makeDefaultName( m_activity ) );
boolean canDoDefaults = solo ||
0 < XWPrefs.getAddrTypes( m_activity ).size();
int iconResID = solo ? R.drawable.ic_sologame : R.drawable.ic_multigame;
int titleID = solo ? R.string.new_game : R.string.new_game_networked;
String msg = getString( canDoDefaults ? R.string.new_game_message
: R.string.new_game_message_nodflt );
if ( !solo ) {
msg += "\n\n" + getString( R.string.new_game_message_net );
boolean forceConfig = 2 <= params.length && (Boolean)params[1];
if ( !solo && !forceConfig && XwJNI.hasKnownPlayers() ) {
dialog = mkNewWithKnowns();
} else {
dialog = mkNewGameDialog( solo );
}
TextView tmpEdit = (TextView)view.findViewById( R.id.msg );
tmpEdit.setText( msg );
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
String name = edit.getText().toString();
curThis().makeThenLaunchOrConfigure( name, true, false );
}
};
ab = makeAlertBuilder()
.setView( view )
.setTitle( titleID )
.setIcon( iconResID )
.setPositiveButton( R.string.newgame_configure_first, lstnr );
if ( canDoDefaults ) {
lstnr2 = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
String name = edit.getText().toString();
curThis().makeThenLaunchOrConfigure( name, false, false );
}
};
ab.setNegativeButton( R.string.use_defaults, lstnr2 );
}
dialog = ab.create();
}
break;
case GAMES_LIST_NAME_REMATCH: {
@ -916,7 +883,7 @@ public class GamesListDelegate extends ListDelegateBase
if ( null != m_rematchExtras ) {
EditWClear edit = (EditWClear)view.findViewById( R.id.edit );
edit.setText( m_rematchExtras.getString( REMATCH_NEWNAME_EXTRA ));
boolean solo = m_rematchExtras.getBoolean( REMATCH_IS_SOLO, true );
solo = m_rematchExtras.getBoolean( REMATCH_IS_SOLO, true );
if ( !solo ) {
iconResID = R.drawable.ic_multigame;
}
@ -947,6 +914,81 @@ public class GamesListDelegate extends ListDelegateBase
return dialog;
} // makeDialog
private Dialog mkNewGameDialog( boolean solo )
{
final LinearLayout view = (LinearLayout)
LocUtils.inflate( m_activity, R.layout.msg_label_and_edit );
final EditWClear edit = (EditWClear)view.findViewById( R.id.edit );
edit.setText( GameUtils.makeDefaultName( m_activity ) );
boolean canDoDefaults = solo ||
0 < XWPrefs.getAddrTypes( m_activity ).size();
int iconResID = solo ? R.drawable.ic_sologame : R.drawable.ic_multigame;
int titleID = solo ? R.string.new_game : R.string.new_game_networked;
String msg = getString( canDoDefaults ? R.string.new_game_message
: R.string.new_game_message_nodflt );
if ( !solo ) {
msg += "\n\n" + getString( R.string.new_game_message_net );
}
TextView tmpEdit = (TextView)view.findViewById( R.id.msg );
tmpEdit.setText( msg );
OnClickListener lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
String name = edit.getText().toString();
curThis().makeThenLaunchOrConfigure( name, true, false );
}
};
AlertDialog.Builder ab = makeAlertBuilder()
.setView( view )
.setTitle( titleID )
.setIcon( iconResID )
.setPositiveButton( R.string.newgame_configure_first, lstnr );
if ( canDoDefaults ) {
OnClickListener lstnr2 = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
String name = edit.getText().toString();
curThis().makeThenLaunchOrConfigure( name, false, false );
}
};
ab.setNegativeButton( R.string.use_defaults, lstnr2 );
}
return ab.create();
}
private Dialog mkNewWithKnowns()
{
String[] names = XwJNI.kplr_getPlayers();
final NewWithKnowns view = (NewWithKnowns)
LocUtils.inflate( m_activity, R.layout.new_game_with_knowns );
view.setNames( names, GameUtils.makeDefaultName( m_activity ) );
AlertDialog.Builder ab = makeAlertBuilder()
.setView( view )
.setTitle( R.string.new_game_networked )
.setIcon( R.drawable.ic_multigame )
.setPositiveButton( "Play Now", new OnClickListener() {
@Override
public void onClick( DialogInterface dlg, int item ) {
String player = view.getSelPlayer();
CommsAddrRec rec = XwJNI.kplr_getAddr( player );
String gameName = view.gameName();
launchLikeRematch( rec, gameName );
}
} )
.setNegativeButton( "Configure First", new OnClickListener() {
@Override
public void onClick( DialogInterface dlg, int item ) {
String name = view.gameName();
curThis().makeThenLaunchOrConfigure( name, true, false );
}
} )
;
return ab.create();
}
private void enableMoveGroupButton( DialogInterface dlgi )
{
((AlertDialog)dlgi)
@ -2332,6 +2374,22 @@ public class GamesListDelegate extends ListDelegateBase
return result;
}
private boolean startWithInvitee( Intent intent )
{
boolean result = false;
try {
CommsAddrRec rec = (CommsAddrRec)intent.getSerializableExtra( INVITEE_REC_EXTRA );
if ( null != rec ) {
String name = intent.getStringExtra( REMATCH_NEWNAME_EXTRA );
makeThenLaunchOrConfigure( name, false, false, rec );
}
} catch ( Exception ex ) {
Log.ex( TAG, ex );
}
return result;
}
private boolean startNewNetGame( NetLaunchInfo nli )
{
boolean handled = false;
@ -2749,6 +2807,7 @@ public class GamesListDelegate extends ListDelegateBase
{
Log.d( TAG, "tryStartsFromIntent(extras={%s})", DbgUtils.extrasToString( intent ) );
boolean handled = startFirstHasDict( intent )
|| startWithInvitee( intent )
|| startNewNetGame( intent )
|| startHasGameID( intent )
|| startRematch( intent )
@ -2890,6 +2949,12 @@ public class GamesListDelegate extends ListDelegateBase
private void makeThenLaunchOrConfigure( String name, boolean doConfigure,
boolean skipAsk )
{
makeThenLaunchOrConfigure( name, doConfigure, skipAsk, null );
}
private void makeThenLaunchOrConfigure( String name, boolean doConfigure,
boolean skipAsk, CommsAddrRec rec )
{
if ( skipAsk || !askingChangeName( name, doConfigure ) ) {
long rowID;
@ -2906,6 +2971,10 @@ public class GamesListDelegate extends ListDelegateBase
rowID = GameUtils.makeNewMultiGame( m_activity, groupID, name );
}
if ( null != rec ) {
DBUtils.addRematchInfo( m_activity, rowID, rec );
}
if ( doConfigure ) {
// configure it
GameConfigDelegate.editForResult( getDelegator(),
@ -2963,6 +3032,15 @@ public class GamesListDelegate extends ListDelegateBase
return intent;
}
private void launchLikeRematch( CommsAddrRec rec, String name )
{
Intent intent = makeSelfIntent( m_activity )
.putExtra( INVITEE_REC_EXTRA, (Serializable)rec )
.putExtra( REMATCH_NEWNAME_EXTRA, name )
;
startActivity( intent );
}
public static Intent makeRematchIntent( Context context, long rowid,
long groupID, CurGameInfo gi,
CommsConnTypeSet addrTypes,

View file

@ -0,0 +1,61 @@
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
/*
* Copyright 2020 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.eehouse.android.xw4;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
public class NewWithKnowns extends LinearLayout {
public NewWithKnowns( Context cx, AttributeSet as )
{
super( cx, as );
}
void setNames( String[] knowns, String gameName )
{
final ArrayAdapter<String> adapter =
new ArrayAdapter<>( getContext(), android.R.layout.simple_spinner_item );
for ( String msg : knowns ) {
adapter.add( msg );
}
Spinner spinner = (Spinner)findViewById( R.id.names );
spinner.setAdapter( adapter );
EditText et = (EditText)findViewById( R.id.name_edit );
et.setText( gameName );
}
String getSelPlayer()
{
Spinner spinner = (Spinner)findViewById( R.id.names );
return spinner.getSelectedItem().toString();
}
String gameName()
{
EditText et = (EditText)findViewById( R.id.name_edit );
return et.getText().toString();
}
}

View file

@ -43,7 +43,7 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
public class CommsAddrRec {
public class CommsAddrRec implements java.io.Serializable {
private static final String TAG = CommsAddrRec.class.getSimpleName();
public enum CommsConnType {

View file

@ -51,6 +51,7 @@ public class GameSummary implements Serializable {
public static final String EXTRA_REMATCH_RELAY = "rm_relay";
public static final String EXTRA_REMATCH_P2P = "rm_p2p";
public static final String EXTRA_REMATCH_MQTT = "rm_mqtt";
public static final String EXTRA_REMATCH_ADDR = "rm_addr";
public static final int MSG_FLAGS_NONE = 0;
public static final int MSG_FLAGS_TURN = 1;
@ -538,6 +539,12 @@ public class GameSummary implements Serializable {
return found;
}
public boolean hasInviteInfo()
{
boolean found = null != getStringExtra( EXTRA_REMATCH_ADDR );
return found;
}
private static boolean localTurnNextImpl( int flags, int turn )
{
int flag = 2 << (turn * 2);

View file

@ -165,6 +165,12 @@ public class XwJNI {
dvc_parseMQTTPacket( getJNI().m_ptrGlobals, buf );
}
public static boolean hasKnownPlayers()
{
String[] players = kplr_getPlayers();
return null != players && 0 < players.length;
}
public static String[] kplr_getPlayers()
{
return kplr_getPlayers( getJNI().m_ptrGlobals );

View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<org.eehouse.android.xw4.NewWithKnowns
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="8dp"
>
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Choose your opponent"
/>
<Spinner android:id="@+id/names"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Name your game"
/>
<EditText android:id="@+id/name_edit"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
</org.eehouse.android.xw4.NewWithKnowns>

View file

@ -33,6 +33,7 @@ typedef struct _KPState {
KnownPlayer* players;
XP_U16 nPlayers;
XP_Bool dirty;
XP_Bool inUse;
} KPState;
/* enum { STREAM_VERSION_KP_1, /\* initial *\/ */
@ -75,6 +76,8 @@ loadState( XW_DUtilCtxt* dutil, XWEnv xwe )
stream_destroy( stream, xwe );
}
XP_ASSERT( !state->inUse );
state->inUse = XP_TRUE;
return state;
}
@ -95,6 +98,14 @@ saveState( XW_DUtilCtxt* dutil, XWEnv xwe, KPState* state )
}
}
static void
releaseState( XW_DUtilCtxt* dutil, XWEnv xwe, KPState* state )
{
XP_ASSERT( state->inUse );
saveState( dutil, xwe, state );
state->inUse = XP_FALSE;
}
static const XP_UCHAR*
figureNameFor( XP_U16 posn, const CurGameInfo* gi )
{
@ -158,7 +169,7 @@ kplr_addAddrs( XW_DUtilCtxt* dutil, XWEnv xwe, const CurGameInfo* gi,
XP_LOGFF( "unable to find %dth name", ii );
}
}
saveState( dutil, xwe, state );
releaseState( dutil, xwe, state );
}
return canUse;
@ -169,6 +180,7 @@ kplr_havePlayers( XW_DUtilCtxt* dutil, XWEnv xwe )
{
KPState* state = loadState( dutil, xwe );
XP_Bool result = 0 < state->nPlayers;
releaseState( dutil, xwe, state );
LOG_RETURNF( "%s", boolToStr(result) );
return result;
}
@ -185,6 +197,7 @@ kplr_getPlayers( XW_DUtilCtxt* dutil, XWEnv xwe,
}
}
*nFound = state->nPlayers;
releaseState( dutil, xwe, state );
}
XP_Bool
@ -199,6 +212,7 @@ kplr_getAddr( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* name,
*addr = kp->addr;
}
}
releaseState( dutil, xwe, state );
LOG_RETURNF( "%s", boolToStr(found) );
return found;
}
@ -208,6 +222,7 @@ kplr_cleanup( XW_DUtilCtxt* dutil )
{
KPState** state = (KPState**)&dutil->kpCtxt;
if ( !!*state ) {
XP_ASSERT( !(*state)->inUse );
for ( KnownPlayer* kp = (*state)->players; !!kp; kp = kp->next ) {
XP_FREEP( dutil->mpool, &kp->name );
XP_FREE( dutil->mpool, kp );