snapshot: invite sent by NFC connects two devices using relay with BT

ability passed too (though not yet used.)
This commit is contained in:
Eric House 2014-10-27 07:51:25 -07:00
parent 7a1cd06486
commit 93f0b626af
21 changed files with 1433 additions and 1210 deletions

File diff suppressed because it is too large Load diff

View file

@ -84,7 +84,7 @@
<string name="key_udp_interval">key_udp_interval</string>
<string name="key_notagain_sync">key_notagain_sync</string>
<string name="key_notagain_sms_ready">key_notagain_sms_ready</string>
<!-- <string name="key_notagain_sms_ready">key_notagain_sms_ready</string> -->
<string name="key_notagain_newselect">key_notagain_newselect</string>
<string name="key_notagain_backclears">key_notagain_backclears</string>
<string name="key_notagain_chat">key_notagain_chat</string>

View file

@ -1149,19 +1149,24 @@
<!-- The invitation process begins with this query. The choice is
between html and plaintext formatting but I also provide some
explanation/guidance. -->
<string name="sms_or_email">Send invitation using SMS (texting) or
via email?</string>
<string name="nfc_or_email">Send invitation using NFC (Android
beaming NEW) or via email?</string>
<string name="nfc_or_sms_or_email">Send invitation using SMS
(texting) or NFC (\"Android beaming\" NEW) or via email?</string>
<string name="invite_choice_sms">SMS (texting)</string>
<string name="invite_choice_email">Email</string>
<string name="invite_choice_bt">Bluetooth</string>
<string name="invite_choice_nfc">NFC (\"Android beaming\")</string>
<string name="invite_choice_title">How would you like to send this invitation?</string>
<!-- <string name="sms_or_email">Send invitation using SMS (texting) or -->
<!-- via email?</string> -->
<!-- <string name="nfc_or_email">Send invitation using NFC (Android -->
<!-- beaming NEW) or via email?</string> -->
<!-- <string name="nfc_or_sms_or_email">Send invitation using SMS -->
<!-- (texting) or NFC (\"Android beaming\" NEW) or via email?</string> -->
<!-- When an invitation is sent, the user gets to choose between
plaintext and html formatting. These two strings are shown in the
two buttons in the dialog. -->
<string name="button_text">SMS/Text</string>
<string name="button_html">Email</string>
<string name="button_nfc">NFC</string>
<!-- <string name="button_text">SMS/Text</string> -->
<!-- <string name="button_html">Email</string> -->
<!-- <string name="button_nfc">NFC</string> -->
<!-- This is the subject line of the email/text sent to invite
someone to join a game. -->
@ -2152,10 +2157,10 @@
<string name="sms_ready_text">Tap the receiving device now</string>
<string name="not_again_sms_ready">You have NFC enabled. That
means that any time a board that\'s missing a player is open, you
can tap a nearby person\'s device to invite him/her to
play if he/she is also using NFC.</string>
<!-- <string name="not_again_sms_ready">You have NFC enabled. That -->
<!-- means that any time a board that\'s missing a player is open, you -->
<!-- can tap a nearby person\'s device to invite him/her to -->
<!-- play if he/she is also using NFC.</string> -->
<string name="pct_suffix">\u0020pct.</string>

View file

@ -981,18 +981,23 @@
<!-- The invitation process begins with this query. The choice is
between html and plaintext formatting but I also provide some
explanation/guidance. -->
<string name="sms_or_email">Dnes noitativni gnisu SMS )gnitxet( ro
aiv ?liame</string>
<string name="nfc_or_email">Dnes noitativni gnisu CFN dIordna(
gnimaeb )WEn ro aiv ?liame</string>
<string name="nfc_or_sms_or_email">Dnes noitativni gnisu SMS
)gnitxet( ro CFN (\"Diordna gnimaeb\" )WEn ro aiv ?liame</string>
<string name="invite_choice_sms">SMS )gnitxet(</string>
<string name="invite_choice_email">Liame</string>
<string name="invite_choice_bt">Htooteulb</string>
<string name="invite_choice_nfc">CFN (\"Diordna gnimaeb\")</string>
<string name="invite_choice_title">Woh dluow uoy ekil ot dnes siht ?noitativni</string>
<!-- <string name="sms_or_email">Send invitation using SMS (texting) or -->
<!-- via email?</string> -->
<!-- <string name="nfc_or_email">Send invitation using NFC (Android -->
<!-- beaming NEW) or via email?</string> -->
<!-- <string name="nfc_or_sms_or_email">Send invitation using SMS -->
<!-- (texting) or NFC (\"Android beaming\" NEW) or via email?</string> -->
<!-- When an invitation is sent, the user gets to choose between
plaintext and html formatting. These two strings are shown in the
two buttons in the dialog. -->
<string name="button_text">TXEt/sms</string>
<string name="button_html">Liame</string>
<string name="button_nfc">CFN</string>
<!-- <string name="button_text">SMS/Text</string> -->
<!-- <string name="button_html">Email</string> -->
<!-- <string name="button_nfc">NFC</string> -->
<!-- This is the subject line of the email/text sent to invite
someone to join a game. -->
<string name="invite_subject_fmt">Tel\'s yalp Sdrowssorc mOor( %1$s)</string>
@ -1845,10 +1850,10 @@
<string name="no_hide_titlebar">Siht gnittes si derongi no secived
ekil sruoy taht dneped no eht \"Noitca rab.\"</string>
<string name="sms_ready_text">Pat eht gniviecer ecived won</string>
<string name="not_again_sms_ready">Uoy evah CFN delbane. Taht
snaem taht yna emit a draob taht\'s gnissim a reyalp si ,nepo uoy
nac pat a ybraen nosrep\'s ecived ot etivni reh/mih ot
yalp fi ehs/eh si osla gnisu CFN.</string>
<!-- <string name="not_again_sms_ready">You have NFC enabled. That -->
<!-- means that any time a board that\'s missing a player is open, you -->
<!-- can tap a nearby person\'s device to invite him/her to -->
<!-- play if he/she is also using NFC.</string> -->
<string name="pct_suffix">\u0020tcp.</string>
<string name="menu_rateme">Etar Sdrowssorc</string>
<string name="no_market">Elgoog Yalp ppa ton dnuof</string>

View file

@ -981,18 +981,23 @@
<!-- The invitation process begins with this query. The choice is
between html and plaintext formatting but I also provide some
explanation/guidance. -->
<string name="sms_or_email">SEND INVITATION USING SMS (TEXTING) OR
VIA EMAIL?</string>
<string name="nfc_or_email">SEND INVITATION USING NFC (ANDROID
BEAMING NEW) OR VIA EMAIL?</string>
<string name="nfc_or_sms_or_email">SEND INVITATION USING SMS
(TEXTING) OR NFC (\"ANDROID BEAMING\" NEW) OR VIA EMAIL?</string>
<string name="invite_choice_sms">SMS (TEXTING)</string>
<string name="invite_choice_email">EMAIL</string>
<string name="invite_choice_bt">BLUETOOTH</string>
<string name="invite_choice_nfc">NFC (\"ANDROID BEAMING\")</string>
<string name="invite_choice_title">HOW WOULD YOU LIKE TO SEND THIS INVITATION?</string>
<!-- <string name="sms_or_email">Send invitation using SMS (texting) or -->
<!-- via email?</string> -->
<!-- <string name="nfc_or_email">Send invitation using NFC (Android -->
<!-- beaming NEW) or via email?</string> -->
<!-- <string name="nfc_or_sms_or_email">Send invitation using SMS -->
<!-- (texting) or NFC (\"Android beaming\" NEW) or via email?</string> -->
<!-- When an invitation is sent, the user gets to choose between
plaintext and html formatting. These two strings are shown in the
two buttons in the dialog. -->
<string name="button_text">SMS/TEXT</string>
<string name="button_html">EMAIL</string>
<string name="button_nfc">NFC</string>
<!-- <string name="button_text">SMS/Text</string> -->
<!-- <string name="button_html">Email</string> -->
<!-- <string name="button_nfc">NFC</string> -->
<!-- This is the subject line of the email/text sent to invite
someone to join a game. -->
<string name="invite_subject_fmt">LET\'S PLAY CROSSWORDS (ROOM %1$s)</string>
@ -1845,10 +1850,10 @@
<string name="no_hide_titlebar">THIS SETTING IS IGNORED ON DEVICES
LIKE YOURS THAT DEPEND ON THE \"ACTION BAR.\"</string>
<string name="sms_ready_text">TAP THE RECEIVING DEVICE NOW</string>
<string name="not_again_sms_ready">YOU HAVE NFC ENABLED. THAT
MEANS THAT ANY TIME A BOARD THAT\'S MISSING A PLAYER IS OPEN, YOU
CAN TAP A NEARBY PERSON\'S DEVICE TO INVITE HIM/HER TO
PLAY IF HE/SHE IS ALSO USING NFC.</string>
<!-- <string name="not_again_sms_ready">You have NFC enabled. That -->
<!-- means that any time a board that\'s missing a player is open, you -->
<!-- can tap a nearby person\'s device to invite him/her to -->
<!-- play if he/she is also using NFC.</string> -->
<string name="pct_suffix">\u0020PCT.</string>
<string name="menu_rateme">RATE CROSSWORDS</string>
<string name="no_market">GOOGLE PLAY APP NOT FOUND</string>

View file

@ -1,83 +0,0 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2009-2011 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.Intent;
import android.os.Bundle;
import org.json.JSONException;
import org.json.JSONObject;
public class AbsLaunchInfo {
private static final String VALID = "abslaunchinfo_valid";
protected String dict;
protected int lang;
protected int nPlayersT;
private boolean m_valid;
protected AbsLaunchInfo() {}
protected JSONObject init( String data ) throws JSONException
{
JSONObject json = new JSONObject( data );
lang = json.getInt( MultiService.LANG );
dict = json.getString( MultiService.DICT );
nPlayersT = json.getInt( MultiService.NPLAYERST );
return json;
}
protected void init( Intent intent )
{
Bundle bundle = intent.getExtras();
init( bundle );
}
protected void init( Bundle bundle )
{
lang = bundle.getInt( MultiService.LANG );
dict = bundle.getString( MultiService.DICT );
nPlayersT = bundle.getInt( MultiService.NPLAYERST );
m_valid = bundle.getBoolean( VALID );
}
protected void putSelf( Bundle bundle )
{
bundle.putInt( MultiService.LANG, lang );
bundle.putString( MultiService.DICT, dict );
bundle.putInt( MultiService.NPLAYERST, nPlayersT );
bundle.putBoolean( VALID, m_valid );
}
protected static JSONObject makeLaunchJSONObject( int lang, String dict,
int nPlayersT )
throws JSONException
{
return new JSONObject()
.put( MultiService.LANG, lang )
.put( MultiService.DICT, dict )
.put( MultiService.NPLAYERST, nPlayersT )
;
}
protected boolean isValid() { return m_valid; }
protected void setValid( boolean valid ) { m_valid = valid; }
}

View file

@ -1,101 +0,0 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/*
* Copyright 2009-2011 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.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import org.json.JSONException;
import org.json.JSONObject;
import junit.framework.Assert;
import org.eehouse.android.xw4.jni.CommsAddrRec;
public class BTLaunchInfo extends AbsLaunchInfo {
protected String btName;
protected String btAddress;
protected int gameID;
public BTLaunchInfo( String data )
{
try {
JSONObject json = init( data );
gameID = json.getInt( MultiService.GAMEID );
btName = json.getString( MultiService.BT_NAME );
btAddress = json.getString( MultiService.BT_ADDRESS );
setValid( true );
} catch ( JSONException ex ) {
DbgUtils.loge( ex );
}
}
public static String makeLaunchJSON( String curJson, int gameID, int lang,
String dict, int nPlayersT )
{
Assert.assertNull( curJson );
String result = null;
BluetoothAdapter adapter = XWApp.BTSUPPORTED
? BluetoothAdapter.getDefaultAdapter() : null;
if ( null != adapter ) {
String name = adapter.getName();
String address = adapter.getAddress();
try {
result = makeLaunchJSONObject( lang, dict, nPlayersT )
.put( MultiService.GAMEID, gameID )
.put( MultiService.BT_NAME, name )
.put( MultiService.BT_ADDRESS, address )
.toString();
} catch ( org.json.JSONException jse ) {
DbgUtils.loge( jse );
}
}
return result;
}
public BTLaunchInfo( Intent intent )
{
init( intent );
btName = intent.getStringExtra( MultiService.BT_NAME );
btAddress = intent.getStringExtra( MultiService.BT_ADDRESS );
gameID = intent.getIntExtra( MultiService.GAMEID, 0 );
setValid( null != btAddress && 0 != gameID );
}
public static void putExtras( Intent intent, int gameID,
String btName, String btAddr )
{
intent.putExtra( MultiService.GAMEID, gameID );
intent.putExtra( MultiService.BT_NAME, btName );
intent.putExtra( MultiService.BT_ADDRESS, btAddr );
intent.putExtra( MultiService.OWNER, MultiService.OWNER_BT );
}
public CommsAddrRec getAddrRec()
{
return new CommsAddrRec( btName, btAddress );
}
}

View file

@ -125,11 +125,13 @@ public class BTService extends XWService {
}
public BTQueueElem( BTCmd cmd, byte[] buf, String btAddr, int gameID ) {
this( cmd );
Assert.assertTrue( null != btAddr && 0 < btAddr.length() );
m_msg = buf; m_btAddr = btAddr;
m_gameID = gameID;
}
public BTQueueElem( BTCmd cmd, String btAddr, int gameID ) {
this( cmd );
Assert.assertTrue( null != btAddr && 0 < btAddr.length() );
m_btAddr = btAddr;
m_gameID = gameID;
}
@ -157,6 +159,13 @@ public class BTService extends XWService {
return null != adapter && adapter.isEnabled();
}
public static String[] getBTNameAndAddress()
{
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
return null == adapter ? null
: new String[] { adapter.getName(), adapter.getAddress() };
}
public static int getPairedCount( Activity activity )
{
int result = 0;
@ -242,7 +251,7 @@ public class BTService extends XWService {
context.startService( intent );
}
public static void gotGameViaNFC( Context context, BTLaunchInfo bli )
public static void gotGameViaNFC( Context context, NetLaunchInfo bli )
{
Intent intent = getIntentTo( context, NFCINVITE );
intent.putExtra( GAMEID_STR, bli.gameID );
@ -258,12 +267,18 @@ public class BTService extends XWService {
public static int enqueueFor( Context context, byte[] buf,
String targetAddr, int gameID )
{
int nSent = -1;
if ( null != targetAddr && 0 < targetAddr.length() ) {
Intent intent = getIntentTo( context, SEND );
intent.putExtra( MSG_STR, buf );
intent.putExtra( ADDR_STR, targetAddr );
intent.putExtra( GAMEID_STR, gameID );
context.startService( intent );
return buf.length;
nSent = buf.length;
} else {
DbgUtils.logf( "BTService.enqueueFor(): targetAddr is null" );
}
return nSent;
}
public static void gameDied( Context context, int gameID )
@ -1015,7 +1030,8 @@ public class BTService extends XWService {
Intent intent = MultiService
.makeMissingDictIntent( context, gameName, lang, dict,
nPlayersT, nPlayersH );
BTLaunchInfo.putExtras( intent, gameID, btName, btAddr );
Assert.fail();
// NetLaunchInfo.putExtras( intent, gameID, btName, btAddr );
MultiService.postMissingDictNotification( context, intent,
gameID );
result = BTCmd.INVITE_ACCPT; // ???

View file

@ -51,6 +51,7 @@ import java.util.concurrent.Semaphore;
import junit.framework.Assert;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify.InviteMeans;
import org.eehouse.android.xw4.jni.*;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
@ -106,6 +107,7 @@ public class BoardDelegate extends DelegateBase
private String[] m_texts;
private CommsConnTypeSet m_connTypes = null;
private String[] m_missingDevs;
private InviteMeans m_missingMeans = null;
private boolean m_progressShown = false;
private String m_curTiles;
private boolean m_canUndoTiles;
@ -450,7 +452,7 @@ public class BoardDelegate extends DelegateBase
.create();
break;
case DLG_INVITE:
if ( null != m_room ) {
if ( true || null != m_room ) {
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
@ -462,6 +464,8 @@ public class BoardDelegate extends DelegateBase
.setPositiveButton( R.string.button_yes, lstnr )
.setNegativeButton( R.string.button_no, null )
.create();
} else {
DbgUtils.showf( m_activity, "can't invite for relay: room name not set" );
}
break;
@ -481,7 +485,7 @@ public class BoardDelegate extends DelegateBase
protected void prepareDialog( DlgID dlgID, Dialog dialog )
{
switch( dlgID ) {
case DLG_INVITE:
case DLG_INVITE: // here
AlertDialog ad = (AlertDialog)dialog;
String message =
getString( R.string.invite_msg_fmt, m_nMissing );
@ -621,11 +625,13 @@ public class BoardDelegate extends DelegateBase
// onActivityResult is called immediately *before*
// onResume -- meaning m_gi etc are still null.
m_missingDevs = data.getStringArrayExtra( BTInviteDelegate.DEVS );
m_missingMeans = InviteMeans.BLUETOOTH;
break;
case SMS_INVITE_RESULT:
// onActivityResult is called immediately *before*
// onResume -- meaning m_gi etc are still null.
m_missingDevs = data.getStringArrayExtra( SMSInviteDelegate.DEVS );
m_missingMeans = InviteMeans.SMS;
break;
}
}
@ -905,24 +911,7 @@ public class BoardDelegate extends DelegateBase
@Override
public void dlgButtonClicked( Action action, int which, Object[] params )
{
if ( Action.LAUNCH_INVITE_ACTION == action ) {
if ( DlgDelegate.DISMISS_BUTTON != which ) {
if ( DlgDelegate.NFC_BTN == which ) {
if ( NFCUtils.nfcAvail( m_activity )[1] ) {
showNotAgainDlgThen( R.string.not_again_sms_ready,
R.string.key_notagain_sms_ready );
} else {
showDialog( DlgID.ENABLE_NFC );
}
} else {
String inviteID = GameUtils.formatGameID( m_gi.gameID );
GameUtils.launchInviteActivity( m_activity, which, m_room,
inviteID, m_gi.dictLang,
m_gi.dictName,
m_gi.nPlayers );
}
}
} else if ( AlertDialog.BUTTON_POSITIVE == which ) {
if ( AlertDialog.BUTTON_POSITIVE == which ) {
JNICmd cmd = JNICmd.CMD_NONE;
switch ( action ) {
case UNDO_LAST_ACTION:
@ -1001,6 +990,34 @@ public class BoardDelegate extends DelegateBase
}
} // dlgButtonClicked
public void inviteChoiceMade( Action action, InviteMeans means,
Object[] params )
{
if ( action == Action.LAUNCH_INVITE_ACTION ) {
switch( means ) {
case NFC:
if ( ! NFCUtils.nfcAvail( m_activity )[1] ) {
showDialog( DlgID.ENABLE_NFC );
}
break;
case BLUETOOTH:
BTInviteDelegate.launchForResult( m_activity, m_nMissing,
BT_INVITE_RESULT );
break;
case SMS:
SMSInviteDelegate.launchForResult( m_activity, m_nMissing,
SMS_INVITE_RESULT );
break;
case EMAIL:
NetLaunchInfo nli = new NetLaunchInfo( m_summary, m_gi );
GameUtils.launchEmailInviteActivity( m_activity, nli );
break;
default:
Assert.fail();
}
}
}
//////////////////////////////////////////////////
// View.OnClickListener interface
//////////////////////////////////////////////////
@ -1167,32 +1184,35 @@ public class BoardDelegate extends DelegateBase
public String makeNFCMessage()
{
String data = null;
if ( 0 < m_nMissing ) { // Isn't there a better test??
NetLaunchInfo nli = new NetLaunchInfo( m_gi.gameID, m_gi.dictLang,
m_gi.dictName, m_gi.nPlayers );
for ( Iterator<CommsConnType> iter = m_connTypes.iterator();
iter.hasNext(); ) {
CommsConnType typ = iter.next();
switch ( typ ) {
case COMMS_CONN_RELAY:
if ( 0 < m_nMissing ) { // Isn't there a better test??
String room = m_summary.roomName;
String inviteID = String.format( "%X", m_gi.gameID );
Assert.assertNotNull( room );
data = NetLaunchInfo.makeLaunchJSON( data, room, inviteID, m_gi.dictLang,
m_gi.dictName, m_gi.nPlayers );
removeDialog( DlgID.DLG_INVITE );
}
String inviteID = String.format( "%X", m_gi.gameID );
nli.addRelayInfo( room, inviteID );
break;
case COMMS_CONN_BT:
if ( 0 < m_nMissing ) {
data = BTLaunchInfo.makeLaunchJSON( data, m_gi.gameID, m_gi.dictLang,
m_gi.dictName, m_gi.nPlayers );
removeDialog( DlgID.CONFIRM_THEN );
}
nli.addBTInfo();
break;
case COMMS_CONN_SMS:
nli.addSMSInfo();
break;
default:
DbgUtils.logf( "Not doing NFC join for conn type %s",
typ.toString() );
}
}
data = nli.makeLaunchJSON();
}
if ( null != data ) {
removeDialog( DlgID.CONFIRM_THEN );
}
return data;
}
@ -1314,6 +1334,7 @@ public class BoardDelegate extends DelegateBase
private void handleConndMessage( String room, int devOrder, // <- hostID
boolean allHere, int nMissing )
{
DbgUtils.logf( "BoardDelegate.handleConndMessage(): nMissing = %d", nMissing );
int naMsg = 0;
int naKey = 0;
String toastStr = null;
@ -1652,26 +1673,30 @@ public class BoardDelegate extends DelegateBase
{
m_connTypes = connTypes;
Assert.assertTrue( isServer || 0 == nMissing );
DbgUtils.logf( "BoardDelegate.informMissing(isServer=%b, nMissing = %d)",
isServer, nMissing );
m_nMissing = nMissing; // will be 0 unless isServer is true
Action action = null;
if ( 0 < nMissing && isServer && !m_haveInvited ) {
for ( Iterator<CommsConnType> iter = connTypes.iterator();
null == action && iter.hasNext(); ) {
CommsConnType connType = iter.next();
switch( connType ) {
case COMMS_CONN_BT:
action = Action.BT_PICK_ACTION;
break;
case COMMS_CONN_SMS:
action = Action.SMS_PICK_ACTION;
break;
}
}
}
if ( null != action ) {
nonRelayInvite( action );
// showInviteChoicesThen( Action.LAUNCH_INVITE_ACTION );
showDialog( DlgID.DLG_INVITE );
// for ( Iterator<CommsConnType> iter = connTypes.iterator();
// null == action && iter.hasNext(); ) {
// CommsConnType connType = iter.next();
// switch( connType ) {
// case COMMS_CONN_BT:
// action = Action.BT_PICK_ACTION;
// break;
// case COMMS_CONN_SMS:
// action = Action.SMS_PICK_ACTION;
// break;
// }
// }
}
// if ( null != action ) {
// nonRelayInvite( action );
// }
}
@Override
@ -1779,25 +1804,32 @@ public class BoardDelegate extends DelegateBase
} // class BoardUtilCtxt
private void inviteForMissing() {
outer:
DbgUtils.logf( "BoardDelegate.inviteForMissing()" );
boolean done = false;
for ( Iterator<CommsConnType> iter = m_connTypes.iterator();
iter.hasNext(); ) {
switch( iter.next() ) {
!done && iter.hasNext(); ) {
CommsConnType typ = iter.next();
DbgUtils.logf( "BoardDelegate.inviteForMissing(): got %s", typ.toString() );
switch( typ ) {
case COMMS_CONN_BT:
nonRelayInvite( Action.BT_PICK_ACTION );
break outer;
done = true;
break;
case COMMS_CONN_SMS:
nonRelayInvite( Action.SMS_PICK_ACTION );
break outer;
done = true;
break;
case COMMS_CONN_RELAY:
showDialog( DlgID.DLG_INVITE );
break outer;
done = true;
break;
}
}
}
private void nonRelayInvite( final Action action )
{
DbgUtils.logf( "BoardDelegate.nonRelayInvite()" );
m_haveInvited = true;
post( new Runnable() {
public void run() {
@ -1835,6 +1867,7 @@ public class BoardDelegate extends DelegateBase
byte[] stream = GameUtils.savedGame( m_activity, m_gameLock );
m_gi = new CurGameInfo( m_activity );
XwJNI.gi_from_stream( m_gi, stream );
DbgUtils.logf( "BoardDelegate:after loadGame: gi.nPlayers: %d", m_gi.nPlayers );
String langName = m_gi.langName();
setThis( this );
@ -1859,10 +1892,12 @@ public class BoardDelegate extends DelegateBase
dictNames, pairs.m_bytes,
pairs.m_paths, langName );
}
m_summary = new GameSummary( m_activity, m_gi );
XwJNI.game_summarize( m_jniGamePtr, m_summary );
DbgUtils.logf( "BoardDelegate:after makeFromStream: room name: %s",
m_summary.roomName );
Handler handler = new Handler() {
public void handleMessage( Message msg ) {
switch( msg.what ) {
@ -2207,17 +2242,18 @@ public class BoardDelegate extends DelegateBase
{
if ( XWApp.BTSUPPORTED || XWApp.SMSSUPPORTED ) {
if ( null != m_missingDevs ) {
Assert.assertNotNull( m_missingMeans );
String gameName = GameUtils.getName( m_activity, m_rowid );
m_invitesPending = m_missingDevs.length;
for ( String dev : m_missingDevs ) {
if ( m_connTypes.contains( CommsConnType.COMMS_CONN_BT ) ) {
switch ( m_missingMeans ) {
case BLUETOOTH:
String progMsg = BTService.nameForAddr( dev );
progMsg = getString( R.string.invite_progress_fmt, progMsg );
m_progressShown = true;
startProgress( R.string.invite_progress_title, progMsg,
new DialogInterface.OnCancelListener() {
public void
onCancel( DialogInterface dlg )
public void onCancel( DialogInterface dlg )
{
m_progressShown = false;
}
@ -2227,14 +2263,17 @@ public class BoardDelegate extends DelegateBase
gameName, m_gi.dictLang,
m_gi.dictName, m_gi.nPlayers,
1 );
} else if ( m_connTypes.contains( CommsConnType.COMMS_CONN_SMS ) ) {
break;
case SMS:
SMSService.inviteRemote( m_activity, dev, m_gi.gameID,
gameName, m_gi.dictLang,
m_gi.dictName, m_gi.nPlayers,
1 );
break;
}
}
m_missingDevs = null;
m_missingMeans = null;
}
}
}

View file

@ -667,7 +667,7 @@ public class DBUtils {
// Return creation time of newest game matching this nli, or null
// if none found.
public static Date getMostRecentCreate( Context context, BTLaunchInfo bli )
public static Date getMostRecentCreate( Context context, NetLaunchInfo bli )
{
Date result = null;
String[] selectionArgs = new String[] {
@ -686,37 +686,38 @@ public class DBUtils {
cursor.close();
db.close();
}
DbgUtils.logf( "getMostRecentCreate() => %H", result );
return result;
}
// Return creation time of newest game matching this nli, or null
// if none found.
public static Date getMostRecentCreate( Context context, NetLaunchInfo nli )
{
Date result = null;
String[] selectionArgs = new String[] {
DBHelper.ROOMNAME, nli.room,
DBHelper.INVITEID, nli.inviteID,
DBHelper.DICTLANG, String.format( "%d", nli.lang ),
DBHelper.NUM_PLAYERS, String.format( "%d", nli.nPlayersT )
};
// public static Date getMostRecentCreate( Context context, NetLaunchInfo nli )
// {
// Date result = null;
// String[] selectionArgs = new String[] {
// DBHelper.ROOMNAME, nli.room,
// DBHelper.INVITEID, nli.inviteID,
// DBHelper.DICTLANG, String.format( "%d", nli.lang ),
// DBHelper.NUM_PLAYERS, String.format( "%d", nli.nPlayersT )
// };
initDB( context );
synchronized( s_dbHelper ) {
SQLiteDatabase db = s_dbHelper.getReadableDatabase();
Cursor cursor = db.rawQuery( "SELECT " + DBHelper.CREATE_TIME +
" FROM " + DBHelper.TABLE_NAME_SUM +
" WHERE ?=? AND ?=? AND ?=? AND ?=?",
selectionArgs );
if ( cursor.moveToNext() ) {
int indx = cursor.getColumnIndex( DBHelper.CREATE_TIME );
result = new Date( cursor.getLong( indx ) );
}
cursor.close();
db.close();
}
return result;
}
// initDB( context );
// synchronized( s_dbHelper ) {
// SQLiteDatabase db = s_dbHelper.getReadableDatabase();
// Cursor cursor = db.rawQuery( "SELECT " + DBHelper.CREATE_TIME +
// " FROM " + DBHelper.TABLE_NAME_SUM +
// " WHERE ?=? AND ?=? AND ?=? AND ?=?",
// selectionArgs );
// if ( cursor.moveToNext() ) {
// int indx = cursor.getColumnIndex( DBHelper.CREATE_TIME );
// result = new Date( cursor.getLong( indx ) );
// }
// cursor.close();
// db.close();
// }
// return result;
// }
public static Date getMostRecentCreate( Context context, Uri data )
{
@ -2134,7 +2135,7 @@ public class DBUtils {
}
private static final int BIT_VECTOR_MASK = 0x8000;
private static CommsConnTypeSet intToConnTypeSet( int asInt )
public static CommsConnTypeSet intToConnTypeSet( int asInt )
{
DbgUtils.logf( "intToConnTypeSet(in: %s)", asInt );
CommsConnTypeSet result = new CommsConnTypeSet();
@ -2155,7 +2156,7 @@ public class DBUtils {
return result;
}
private static int connTypeSetToInt( CommsConnTypeSet set )
public static int connTypeSetToInt( CommsConnTypeSet set )
{
DbgUtils.logf( "connTypeSetToInt(setSize: %d)", set.size() );
int result = BIT_VECTOR_MASK;

View file

@ -38,10 +38,11 @@ import android.widget.TextView;
import org.eehouse.android.xw4.DlgDelegate.Action;
import org.eehouse.android.xw4.loc.LocUtils;
import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify;
import junit.framework.Assert;
public class DelegateBase implements DlgDelegate.DlgClickNotify,
public class DelegateBase implements DlgClickNotify,
DlgDelegate.HasDlgDelegate,
MultiService.MultiEventListener {
@ -484,4 +485,10 @@ public class DelegateBase implements DlgDelegate.DlgClickNotify,
{
Assert.fail();
}
public void inviteChoiceMade( Action action, DlgClickNotify.InviteMeans means, Object[] params )
{
Assert.fail();
}
}

View file

@ -33,6 +33,7 @@ import android.os.Handler;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@ -112,7 +113,11 @@ public class DlgDelegate {
private static final String STATE_KEYF = "STATE_%d";
public interface DlgClickNotify {
public static enum InviteMeans {
SMS, EMAIL, NFC, BLUETOOTH,
};
void dlgButtonClicked( Action action, int button, Object[] params );
void inviteChoiceMade( Action action, InviteMeans means, Object[] params );
}
public interface HasDlgDelegate {
void showOKOnlyDialog( int msgID );
@ -313,7 +318,8 @@ public class DlgDelegate {
public void showInviteChoicesThen( final Action action )
{
if ( Utils.deviceSupportsSMS( m_activity )
|| NFCUtils.nfcAvail( m_activity )[0] ) {
|| NFCUtils.nfcAvail( m_activity )[0]
|| BTService.BTAvailable() ) {
DlgState state = new DlgState( DlgID.INVITE_CHOICES_THEN, action );
addState( state );
showDialog( DlgID.INVITE_CHOICES_THEN );
@ -516,32 +522,50 @@ public class DlgDelegate {
return setCallbackDismissListener( dialog, state, dlgID );
}
private Dialog createInviteChoicesDialog( DlgState state, DlgID dlgID )
private Dialog createInviteChoicesDialog( final DlgState state, DlgID dlgID )
{
OnClickListener lstnr = mkCallbackClickListener( state );
boolean haveSMS = Utils.deviceSupportsSMS( m_activity );
boolean haveNFC = NFCUtils.nfcAvail( m_activity )[0];
int msgID;
if ( haveSMS && haveNFC ) {
msgID = R.string.nfc_or_sms_or_email;
} else if ( haveSMS ) {
msgID = R.string.sms_or_email;
} else {
msgID = R.string.nfc_or_email;
final ArrayList<DlgClickNotify.InviteMeans> means =
new ArrayList<DlgClickNotify.InviteMeans>();
ArrayList<String> items = new ArrayList<String>();
if ( Utils.deviceSupportsSMS( m_activity ) ) {
items.add( getString( R.string.invite_choice_sms ) );
means.add( DlgClickNotify.InviteMeans.SMS );
}
items.add( getString( R.string.invite_choice_email ) );
means.add( DlgClickNotify.InviteMeans.EMAIL );
if ( BTService.BTAvailable() ) {
items.add( getString( R.string.invite_choice_bt ) );
means.add( DlgClickNotify.InviteMeans.BLUETOOTH );
}
if ( NFCUtils.nfcAvail( m_activity )[0] ) {
items.add( getString( R.string.invite_choice_nfc ) );
means.add( DlgClickNotify.InviteMeans.NFC );
}
AlertDialog.Builder builder = LocUtils.makeAlertBuilder( m_activity );
builder.setTitle( R.string.query_title );
builder.setMessage( msgID );
builder.setNegativeButton( R.string.button_html, lstnr );
final int[] sel = { -1 };
OnClickListener selChanged = new OnClickListener() {
public void onClick( DialogInterface dlg, int view ) {
sel[0] = view;
}
};
OnClickListener okClicked = new OnClickListener() {
public void onClick( DialogInterface dlg, int view ) {
Assert.assertTrue( Action.SKIP_CALLBACK != state.m_action );
int indx = sel[0];
if ( 0 <= indx ) {
m_clickCallback.inviteChoiceMade( state.m_action,
means.get(indx),
state.m_params );
}
}
};
if ( haveSMS ) {
builder.setPositiveButton( R.string.button_text, lstnr );
}
if ( haveNFC ) {
builder.setNeutralButton( R.string.button_nfc, lstnr );
}
AlertDialog.Builder builder = LocUtils.makeAlertBuilder( m_activity )
.setTitle( R.string.invite_choice_title )
.setSingleChoiceItems( items.toArray( new String[items.size()] ),
sel[0], selChanged )
.setPositiveButton( R.string.button_ok, okClicked )
.setNegativeButton( R.string.button_cancel, null );
return setCallbackDismissListener( builder.create(), state, dlgID );
}

View file

@ -49,6 +49,7 @@ import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnType;
import org.eehouse.android.xw4.jni.CommsAddrRec.CommsConnTypeSet;
import org.eehouse.android.xw4.jni.CurGameInfo.DeviceRole;
import org.eehouse.android.xw4.jni.LastMoveInfo;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify.InviteMeans;
public class GameUtils {
@ -451,6 +452,17 @@ public class GameUtils {
return rowid;
}
public static long makeNewMultiGame( Context context, NetLaunchInfo nli )
{
DbgUtils.logf( "makeNewMultiGame(nli=%s)", nli.toString() );
CommsAddrRec addr = nli.makeAddrRec( context );
return makeNewMultiGame( context, null, DBUtils.GROUPID_UNSPEC, addr,
new int[] {nli.lang}, new String[] { nli.dict },
nli.nPlayersT, 1, nli.inviteID,
nli.gameID, false );
}
private static long makeNewMultiGame( Context context, long groupID,
CommsAddrRec addr,
int[] lang, String[] dict,
@ -507,9 +519,10 @@ public class GameUtils {
int relayPort = XWPrefs.getDefaultRelayPort( context );
CommsAddrRec addr = new CommsAddrRec( relayName, relayPort );
addr.ip_relay_invite = room;
addr.conTypes.add( CommsConnType.COMMS_CONN_BT );
return makeNewMultiGame( context, groupID, addr, lang, dict,
nPlayersT, nPlayersH, inviteID, 0, false );
nPlayersT, nPlayersH, inviteID, 0, true );
}
public static long makeNewRelayGame( Context context, long groupID,
@ -555,11 +568,13 @@ public class GameUtils {
lang, dict, nPlayersT, nPlayersH );
}
public static long makeNewBTGame( Context context, BTLaunchInfo bli )
public static long makeNewBTGame( Context context, NetLaunchInfo nli )
{
return makeNewBTGame( context, null, DBUtils.GROUPID_UNSPEC, bli.gameID,
bli.getAddrRec(), bli.lang, bli.dict,
bli.nPlayersT, 1 );
Assert.fail();
return -1;
// return makeNewBTGame( context, null, DBUtils.GROUPID_UNSPEC, nli.gameID,
// nli.btAddress, nli.lang, nli.dict,
// nli.nPlayersT, 1 );
}
public static long makeNewBTGame( Context context, MultiMsgSink sink,
@ -567,17 +582,19 @@ public class GameUtils {
int lang, String dict,
int nPlayersT, int nPlayersH )
{
long rowid = -1;
int[] langa = { lang };
String[] dicta = { dict };
boolean isHost = null == addr;
if ( isHost ) {
addr = new CommsAddrRec( null, null );
}
String inviteID = GameUtils.formatGameID( gameID );
return makeNewMultiGame( context, sink, groupID, addr, langa, dicta,
nPlayersT, nPlayersH, inviteID, gameID,
isHost );
Assert.fail();
return -1;
// long rowid = -1;
// int[] langa = { lang };
// String[] dicta = { dict };
// boolean isHost = null == addr;
// if ( isHost ) {
// addr = new CommsAddrRec( null, null );
// }
// String inviteID = GameUtils.formatGameID( gameID );
// return makeNewMultiGame( context, sink, groupID, addr, langa, dicta,
// nPlayersT, nPlayersH, inviteID, gameID,
// isHost );
}
public static long makeNewSMSGame( Context context, int gameID,
@ -607,33 +624,22 @@ public class GameUtils {
isHost );
}
public static void launchInviteActivity( Activity activity, int chosen,
String room, String inviteID,
int lang, String dict,
int nPlayers )
public static void launchEmailInviteActivity( Activity activity, NetLaunchInfo nli )
{
Assert.assertNotNull( inviteID );
DbgUtils.logf( "launchEmailInviteActivity: nli=%s", nli.makeLaunchJSON() );
Uri gameUri = nli.makeLaunchUri( activity );
DbgUtils.logf( "launchEmailInviteActivity: uri=%s", gameUri );
if ( DlgDelegate.NFC_BTN == chosen ) {
Utils.showToast( activity, R.string.sms_ready_text );
} else {
Uri gameUri = NetLaunchInfo.makeLaunchUri( activity, room, inviteID,
lang, dict, nPlayers );
String msgString = null == gameUri ? null : gameUri.toString();
if ( null != msgString ) {
boolean choseEmail = DlgDelegate.EMAIL_BTN == chosen;
int fmtId = choseEmail? R.string.invite_htm_fmt : R.string.invite_txt_fmt;
int choiceID;
String message = LocUtils.getString( activity, fmtId, msgString );
String message = LocUtils.getString( activity, R.string.invite_htm_fmt, msgString );
Intent intent = new Intent();
if ( choseEmail ) {
intent.setAction( Intent.ACTION_SEND );
String subject =
LocUtils.getString( activity, R.string.invite_subject_fmt,
room );
nli.room );
intent.putExtra( Intent.EXTRA_SUBJECT, subject );
intent.putExtra( Intent.EXTRA_TEXT, Html.fromHtml(message) );
@ -641,8 +647,7 @@ public class GameUtils {
File tmpdir = XWApp.ATTACH_SUPPORTED ?
DictUtils.getDownloadDir( activity ) : null;
if ( null != tmpdir ) { // no attachment
attach = makeJsonFor( tmpdir, room, inviteID, lang,
dict, nPlayers );
attach = makeJsonFor( tmpdir, nli );
}
if ( null == attach ) { // no attachment
@ -654,22 +659,80 @@ public class GameUtils {
intent.putExtra( Intent.EXTRA_STREAM, uri );
}
choiceID = R.string.invite_chooser_email;
} else {
intent.setAction( Intent.ACTION_VIEW );
intent.setType( "vnd.android-dir/mms-sms" );
intent.putExtra( "sms_body", message );
choiceID = R.string.invite_chooser_sms;
}
String choiceType = LocUtils.getString( activity, choiceID );
String choiceType = LocUtils.getString( activity, R.string.invite_chooser_email );
String chooserMsg =
LocUtils.getString( activity, R.string.invite_chooser_fmt,
choiceType );
activity.startActivity( Intent.createChooser( intent, chooserMsg ) );
}
}
}
// public static void launchInviteActivity( Activity activity,
// InviteMeans means,
// String room, String inviteID,
// int lang, String dict,
// int nPlayers )
// {
// Assert.assertNotNull( inviteID );
// if ( InviteMeans.NFC == means ) {
// Utils.showToast( activity, R.string.sms_ready_text );
// } else {
// // NetLaunchInfo nli = new NetLaunchInfo( 0, lang, dict, nPlayers );
// Uri gameUri = NetLaunchInfo.makeLaunchUri( activity, room, inviteID,
// lang, dict, nPlayers );
// String msgString = null == gameUri ? null : gameUri.toString();
// if ( null != msgString ) {
// boolean choseEmail = InviteMeans.EMAIL == means;
// int fmtId = choseEmail? R.string.invite_htm_fmt : R.string.invite_txt_fmt;
// int choiceID;
// String message = LocUtils.getString( activity, fmtId, msgString );
// Intent intent = new Intent();
// if ( choseEmail ) {
// intent.setAction( Intent.ACTION_SEND );
// String subject =
// LocUtils.getString( activity, R.string.invite_subject_fmt,
// room );
// intent.putExtra( Intent.EXTRA_SUBJECT, subject );
// intent.putExtra( Intent.EXTRA_TEXT, Html.fromHtml(message) );
// File attach = null;
// File tmpdir = XWApp.ATTACH_SUPPORTED ?
// DictUtils.getDownloadDir( activity ) : null;
// if ( null != tmpdir ) { // no attachment
// attach = makeJsonFor( tmpdir, room, inviteID, lang,
// dict, nPlayers );
// }
// if ( null == attach ) { // no attachment
// intent.setType( "message/rfc822");
// } else {
// String mime = LocUtils.getString( activity, R.string.invite_mime );
// intent.setType( mime );
// Uri uri = Uri.fromFile( attach );
// intent.putExtra( Intent.EXTRA_STREAM, uri );
// }
// choiceID = R.string.invite_chooser_email;
// } else {
// intent.setAction( Intent.ACTION_VIEW );
// intent.setType( "vnd.android-dir/mms-sms" );
// intent.putExtra( "sms_body", message );
// choiceID = R.string.invite_chooser_sms;
// }
// String choiceType = LocUtils.getString( activity, choiceID );
// String chooserMsg =
// LocUtils.getString( activity, R.string.invite_chooser_fmt,
// choiceType );
// activity.startActivity( Intent.createChooser( intent, chooserMsg ) );
// }
// }
// }
public static String[] dictNames( Context context, long rowid,
int[] missingLang )
@ -822,6 +885,7 @@ public class GameUtils {
XwJNI.comms_resendAll( gamePtr, false, false );
for ( byte[] msg : msgs ) {
Assert.assertNotNull( ret );
draw = XwJNI.game_receiveMessage( gamePtr, msg, ret )
|| draw;
}
@ -1049,22 +1113,14 @@ public class GameUtils {
}
}
private static File makeJsonFor( File dir, String room, String inviteID,
int lang, String dict, int nPlayers )
private static File makeJsonFor( File dir, NetLaunchInfo nli )
{
File result = null;
if ( XWApp.ATTACH_SUPPORTED ) {
JSONObject json = new JSONObject();
try {
json.put( MultiService.ROOM, room );
json.put( MultiService.INVITEID, inviteID );
json.put( MultiService.LANG, lang );
json.put( MultiService.DICT, dict );
json.put( MultiService.NPLAYERST, nPlayers );
byte[] data = json.toString().getBytes();
byte[] data = nli.makeLaunchJSON().getBytes();
File file = new File( dir,
String.format("invite_%s", room ) );
File file = new File( dir, String.format("invite_%d", nli.gameID ));
try {
FileOutputStream fos = new FileOutputStream( file );
fos.write( data, 0, data.length );
fos.close();

View file

@ -555,7 +555,7 @@ public class GamesListDelegate extends ListDelegateBase
private long[] m_rowids;
private long m_groupid;
private String m_nameField;
private AbsLaunchInfo m_netLaunchInfo;
private NetLaunchInfo m_netLaunchInfo;
private GameNamer m_namer;
private Set<Long> m_launchedGames;
private boolean m_menuPrepared;
@ -1460,7 +1460,7 @@ public class GamesListDelegate extends ListDelegateBase
}
}
private boolean checkWarnNoDict( AbsLaunchInfo nli )
private boolean checkWarnNoDict( NetLaunchInfo nli )
{
// check that we have the dict required
boolean haveDict;
@ -1590,26 +1590,21 @@ public class GamesListDelegate extends ListDelegateBase
NewGameDelegate.startActivity( m_activity, groupID );
}
private void startNewNetGame( AbsLaunchInfo ali )
private void startNewNetGame( NetLaunchInfo nli )
{
Assert.assertTrue( nli.isValid() );
Date create = null;
if ( ali instanceof NetLaunchInfo ) {
create = DBUtils.getMostRecentCreate( m_activity,
(NetLaunchInfo)ali );
} else if ( ali instanceof BTLaunchInfo ) {
create = DBUtils.getMostRecentCreate( m_activity,
(BTLaunchInfo)ali );
}
create = DBUtils.getMostRecentCreate( m_activity, nli );
if ( null == create ) {
if ( checkWarnNoDict( ali ) ) {
makeNewNetGame( ali );
if ( checkWarnNoDict( nli ) ) {
makeNewNetGame( nli );
}
} else if ( XWPrefs.getSecondInviteAllowed( m_activity ) ) {
String msg = getString( R.string.dup_game_query_fmt,
create.toString() );
m_netLaunchInfo = ali;
showConfirmThen( msg, Action.NEW_NET_GAME, ali );
m_netLaunchInfo = nli;
showConfirmThen( msg, Action.NEW_NET_GAME, nli );
} else {
showOKOnlyDialog( R.string.dropped_dupe );
}
@ -1617,20 +1612,17 @@ public class GamesListDelegate extends ListDelegateBase
private void startNewNetGame( Intent intent )
{
AbsLaunchInfo ali = null;
NetLaunchInfo nli = null;
if ( MultiService.isMissingDictIntent( intent ) ) {
ali = new NetLaunchInfo( intent );
if ( !ali.isValid() ) {
ali = new BTLaunchInfo( intent );
}
nli = new NetLaunchInfo( intent );
} else {
Uri data = intent.getData();
if ( null != data ) {
ali = new NetLaunchInfo( m_activity, data );
nli = new NetLaunchInfo( m_activity, data );
}
}
if ( null != ali && ali.isValid() ) {
startNewNetGame( ali );
if ( null != nli && nli.isValid() ) {
startNewNetGame( nli );
}
} // startNewNetGame
@ -1672,18 +1664,10 @@ public class GamesListDelegate extends ListDelegateBase
{
String data = NFCUtils.getFromIntent( intent );
if ( null != data ) {
do {
NetLaunchInfo nli = new NetLaunchInfo( data );
if ( nli.isValid() ) {
startNewNetGame( nli );
break;
}
BTLaunchInfo bli = new BTLaunchInfo( data );
if ( bli.isValid() && checkWarnNoDict( bli ) ) {
BTService.gotGameViaNFC( m_activity, bli );
break;
}
} while ( false );
}
}
@ -1749,11 +1733,7 @@ public class GamesListDelegate extends ListDelegateBase
{
boolean madeGame = null != m_netLaunchInfo;
if ( madeGame ) {
if ( m_netLaunchInfo instanceof NetLaunchInfo ) {
makeNewNetGame( (NetLaunchInfo)m_netLaunchInfo );
} else if ( m_netLaunchInfo instanceof BTLaunchInfo ) {
makeNewBTGame( (BTLaunchInfo)m_netLaunchInfo );
}
makeNewNetGame( m_netLaunchInfo );
m_netLaunchInfo = null;
}
return madeGame;
@ -1842,20 +1822,14 @@ public class GamesListDelegate extends ListDelegateBase
launchGame( rowid, false );
}
private void makeNewNetGame( AbsLaunchInfo ali )
private void makeNewNetGame( NetLaunchInfo nli )
{
long rowid = DBUtils.ROWID_NOTFOUND;
if ( ali instanceof NetLaunchInfo ) {
rowid = GameUtils.makeNewRelayGame( m_activity, (NetLaunchInfo)ali );
} else if ( ali instanceof BTLaunchInfo ) {
rowid = GameUtils.makeNewBTGame( m_activity, (BTLaunchInfo)ali );
} else {
Assert.fail();
}
rowid = GameUtils.makeNewMultiGame( m_activity, nli );
launchGame( rowid, true );
}
private void makeNewBTGame( BTLaunchInfo nli )
private void makeNewBTGame( NetLaunchInfo nli )
{
long rowid = GameUtils.makeNewBTGame( m_activity, nli );
launchGame( rowid, true );

View file

@ -27,46 +27,95 @@ import android.net.Uri;
import android.os.Bundle;
import java.net.URLEncoder;
import java.io.InputStream;
import java.util.Iterator;
import org.json.JSONObject;
import org.json.JSONException;
import junit.framework.Assert;
import org.eehouse.android.xw4.loc.LocUtils;
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.GameSummary;
import org.eehouse.android.xw4.jni.CurGameInfo;
public class NetLaunchInfo extends AbsLaunchInfo {
protected String room;
protected String inviteID;
public class NetLaunchInfo {
private static final String ADDRS_KEY = "ADDRS";
protected String dict;
protected int lang;
protected int nPlayersT;
protected String room; // relay
protected String inviteID; // relay
protected String btName;
protected String btAddress;
protected String phone; // SMS
protected boolean isGSM; // SMS
protected String osVers; // SMS
protected int gameID;
private CommsConnTypeSet m_addrs;
private JSONObject m_json;
private boolean m_valid;
public NetLaunchInfo()
{
m_addrs = new CommsConnTypeSet();
}
public NetLaunchInfo( String data )
{
try {
JSONObject json = init( data );
room = json.getString( MultiService.ROOM );
inviteID = json.getString( MultiService.INVITEID );
setValid( true );
} catch ( JSONException jse ) {
// Don't bother logging; it's just not a valid object of this type
m_json = new JSONObject( data );
int flags = m_json.getInt(ADDRS_KEY);
m_addrs = DBUtils.intToConnTypeSet( flags );
lang = m_json.optInt( MultiService.LANG, -1 );
dict = m_json.optString( MultiService.DICT );
nPlayersT = m_json.optInt( MultiService.NPLAYERST, -1 );
gameID = m_json.optInt( MultiService.GAMEID, -1 );
for ( CommsConnType typ : m_addrs.getTypes() ) {
switch ( typ ) {
case COMMS_CONN_BT:
btAddress = m_json.optString( MultiService.BT_ADDRESS );
btName = m_json.optString( MultiService.BT_NAME );
break;
case COMMS_CONN_RELAY:
room = m_json.optString( MultiService.ROOM );
inviteID = m_json.optString( MultiService.INVITEID );
break;
case COMMS_CONN_SMS:
phone = m_json.optString( "phn" );
isGSM = m_json.optBoolean( "gsm", false );
osVers = m_json.optString( "os" );
break;
default:
DbgUtils.logf( "Unexpected typ %s", typ.toString() );
break;
}
}
public void putSelf( Bundle bundle )
{
super.putSelf( bundle );
bundle.putString( MultiService.ROOM, room );
bundle.putString( MultiService.INVITEID, inviteID );
calcValid();
} catch ( JSONException jse ) {
DbgUtils.loge( jse );
}
}
public NetLaunchInfo( Bundle bundle )
{
init( bundle );
room = bundle.getString( MultiService.ROOM );
inviteID = bundle.getString( MultiService.INVITEID );
Assert.fail();
}
public NetLaunchInfo( Context context, Uri data )
{
setValid( false );
this();
m_valid = false;
if ( null != data ) {
String scheme = data.getScheme();
try {
@ -78,7 +127,7 @@ public class NetLaunchInfo extends AbsLaunchInfo {
byte[] buf = new byte[len];
is.read( buf );
JSONObject json = init( new String( buf ) );
JSONObject json = new JSONObject( new String( buf ) );
room = json.getString( MultiService.ROOM );
inviteID = json.getString( MultiService.INVITEID );
} else {
@ -90,22 +139,139 @@ public class NetLaunchInfo extends AbsLaunchInfo {
String np = data.getQueryParameter( "np" );
nPlayersT = Integer.decode( np );
}
setValid( true );
calcValid();
} catch ( Exception e ) {
DbgUtils.logf( "unable to parse \"%s\"", data.toString() );
}
}
calcValid();
}
public NetLaunchInfo( Intent intent )
{
init( intent );
room = intent.getStringExtra( MultiService.ROOM );
inviteID = intent.getStringExtra( MultiService.INVITEID );
boolean valid = null != room
&& -1 != lang
&& -1 != nPlayersT;
setValid( valid );
lang = intent.getIntExtra( MultiService.LANG, -1 );
dict = intent.getStringExtra( MultiService.DICT );
nPlayersT = intent.getIntExtra( MultiService.NPLAYERST, -1 );
gameID = intent.getIntExtra( MultiService.GAMEID, -1 );
btName = intent.getStringExtra( MultiService.BT_NAME );
btAddress = intent.getStringExtra( MultiService.BT_ADDRESS );
m_addrs = DBUtils.intToConnTypeSet( intent.getIntExtra( ADDRS_KEY, -1 ) );
calcValid();
}
public NetLaunchInfo( int gamID, int dictLang, String dictName, int nPlayers )
{
this();
dict = dictName;
lang = dictLang;
nPlayersT = nPlayers;
gameID = gamID;
}
public NetLaunchInfo( GameSummary summary, CurGameInfo gi )
{
this( gi.gameID, gi.dictLang, gi.dictName, gi.nPlayers );
for ( CommsConnType typ : summary.conTypes.getTypes() ) {
DbgUtils.logf( "NetLaunchInfo(): got type %s", typ.toString() );
switch( typ ) {
case COMMS_CONN_BT:
addBTInfo();
break;
case COMMS_CONN_RELAY:
addRelayInfo( summary.roomName, summary.relayID );
break;
case COMMS_CONN_SMS:
addSMSInfo();
break;
default:
Assert.fail();
break;
}
}
}
public void putSelf( Bundle bundle )
{
bundle.putString( MultiService.ROOM, room );
bundle.putString( MultiService.INVITEID, inviteID );
bundle.putInt( MultiService.LANG, lang );
bundle.putString( MultiService.DICT, dict );
bundle.putInt( MultiService.NPLAYERST, nPlayersT );
bundle.putInt( MultiService.GAMEID, gameID );
bundle.putString( MultiService.BT_NAME, btName );
bundle.putString( MultiService.BT_ADDRESS, btAddress );
int flags = DBUtils.connTypeSetToInt( m_addrs );
bundle.putInt( ADDRS_KEY, flags );
}
public String makeLaunchJSON()
{
String result = null;
try {
result = new JSONObject()
.put( MultiService.LANG, lang )
.put( MultiService.DICT, dict )
.put( MultiService.NPLAYERST, nPlayersT )
.put( MultiService.ROOM, room )
.put( MultiService.INVITEID, inviteID )
.put( MultiService.GAMEID, gameID )
.put( MultiService.BT_NAME, btName )
.put( MultiService.BT_ADDRESS, btAddress )
.put( ADDRS_KEY, DBUtils.connTypeSetToInt( m_addrs ) )
.toString();
} catch ( org.json.JSONException jse ) {
DbgUtils.loge( jse );
}
DbgUtils.logf( "makeLaunchJSON() => %s", result );
return result;
}
public CommsAddrRec makeAddrRec( Context context )
{
CommsAddrRec result = new CommsAddrRec();
for ( CommsConnType typ : m_addrs.getTypes() ) {
result.conTypes.add( typ );
if ( CommsConnType.COMMS_CONN_RELAY == typ ) {
String relayName = XWPrefs.getDefaultRelayHost( context );
int relayPort = XWPrefs.getDefaultRelayPort( context );
result.setRelayParams( relayName, relayPort, room );
}
}
return result;
}
public Uri makeLaunchUri( Context context )
{
Uri.Builder ub = new Uri.Builder()
.scheme( "http" )
.path( String.format( "//%s%s",
LocUtils.getString(context, R.string.invite_host),
LocUtils.getString(context, R.string.invite_prefix) ) )
.appendQueryParameter( "lang", String.format("%d", lang ) )
.appendQueryParameter( "np", String.format( "%d", nPlayersT ) )
.appendQueryParameter( "gid", String.format( "%d", nPlayersT ) );
if ( null != dict ) {
ub.appendQueryParameter( "wl", dict );
}
if ( m_addrs.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
ub.appendQueryParameter( "room", room );
ub.appendQueryParameter( "id", inviteID );
}
if ( m_addrs.contains( CommsConnType.COMMS_CONN_BT ) ) {
ub.appendQueryParameter( "bta", btAddress );
ub.appendQueryParameter( "btn", btName );
}
if ( m_addrs.contains( CommsConnType.COMMS_CONN_SMS ) ) {
ub.appendQueryParameter( "phn", phone );
}
return ub.build();
}
public static Uri makeLaunchUri( Context context, String room,
@ -127,19 +293,100 @@ public class NetLaunchInfo extends AbsLaunchInfo {
return ub.build();
}
public static String makeLaunchJSON( String curJson, String room, String inviteID,
int lang, String dict, int nPlayersT )
// public static String makeLaunchJSON( String curJson, String room, String inviteID,
// int lang, String dict, int nPlayersT )
// {
// Assert.assertNull( curJson );
// String result = null;
// try {
// result = new JSONObject()
// .put( MultiService.LANG, lang )
// .put( MultiService.DICT, dict )
// .put( MultiService.NPLAYERST, nPlayersT )
// .put( MultiService.ROOM, room )
// .put( MultiService.INVITEID, inviteID )
// .put( MultiService.GAMEID, gameID )
// .put( MultiService.BT_NAME, name )
// .put( MultiService.BT_ADDRESS, address )
// .put( ADDRS_KEY, DBUtils.connTypeSetToInt( m_addrs ) )
// .toString();
// } catch ( org.json.JSONException jse ) {
// DbgUtils.loge( jse );
// }
// DbgUtils.logf( "makeLaunchJSON() => %s", result );
// return result;
// }
public void addRelayInfo( String aRoom, String anInviteID )
{
Assert.assertNull( curJson );
String result = null;
try {
result = makeLaunchJSONObject( lang, dict, nPlayersT )
.put( MultiService.ROOM, room )
.put( MultiService.INVITEID, inviteID )
.toString();
} catch ( org.json.JSONException jse ) {
DbgUtils.loge( jse );
room = aRoom;
inviteID = anInviteID;
m_addrs.add( CommsConnType.COMMS_CONN_RELAY );
}
return result;
public void addBTInfo()
{
String[] got = BTService.getBTNameAndAddress();
if ( null != got ) {
btName = got[0];
btAddress = got[1];
m_addrs.add( CommsConnType.COMMS_CONN_BT );
} else {
DbgUtils.logf( "addBTInfo(): no BT info available" );
}
}
public void addSMSInfo()
{
// look up own phone number, which will require new permission
Assert.fail();
phone = "123-456-7890";
m_addrs.add( CommsConnType.COMMS_CONN_SMS );
}
public boolean isValid()
{
DbgUtils.logf( "NetLaunchInfo(%s).isValid() => %b", toString(), m_valid );
return m_valid;
}
@Override
public String toString()
{
return makeLaunchJSON();
}
public static void putExtras( Intent intent, int gameID, String btAddr )
{
Assert.fail();
}
private boolean hasCommon()
{
return null != dict
&& 0 < lang
&& 0 < nPlayersT
&& 0 != gameID;
}
private void calcValid()
{
boolean valid = hasCommon();
for ( Iterator<CommsConnType> iter = m_addrs.iterator();
valid && iter.hasNext(); ) {
switch ( iter.next() ) {
case COMMS_CONN_RELAY:
valid = null != room && null != inviteID;
break;
case COMMS_CONN_BT:
valid = null != btAddress && 0 != gameID;
break;
case COMMS_CONN_SMS:
Assert.fail();
valid = false;
break;
}
}
m_valid = valid;
}
}

View file

@ -42,6 +42,8 @@ import org.eehouse.android.xw4.jni.CurGameInfo;
import org.eehouse.android.xw4.jni.CommonPrefs;
import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.XwJNI;
import org.eehouse.android.xw4.DlgDelegate.DlgClickNotify.InviteMeans;
import org.eehouse.android.xw4.DlgDelegate.Action;
public class NewGameDelegate extends DelegateBase {
@ -151,13 +153,12 @@ public class NewGameDelegate extends DelegateBase {
// DlgDelegate.DlgClickNotify interface
@Override
public void dlgButtonClicked( DlgDelegate.Action action, int which, Object[] params )
public void inviteChoiceMade( Action action, InviteMeans means,
Object[] params )
{
switch( action ) {
case NEW_GAME_ACTION:
if ( DlgDelegate.DISMISS_BUTTON != which ) {
makeNewGame( true, true, which );
}
makeNewGame( true, true, means );
break;
default:
Assert.fail();
@ -297,14 +298,14 @@ public class NewGameDelegate extends DelegateBase {
// Let 'em cancel before we make the game
showInviteChoicesThen( DlgDelegate.Action.NEW_GAME_ACTION );
} else {
makeNewGame( networked, launch, DlgDelegate.SMS_BTN );
makeNewGame( networked, launch, InviteMeans.SMS );
}
}
private void makeNewGame( boolean networked, boolean launch,
int inviteHow )
InviteMeans inviteHow )
{
boolean viaNFC = DlgDelegate.NFC_BTN == inviteHow;
boolean viaNFC = InviteMeans.NFC == inviteHow;
if ( viaNFC && !NFCUtils.nfcAvail( m_activity )[1] ) {
showDialog( DlgID.ENABLE_NFC );
} else {
@ -329,9 +330,10 @@ public class NewGameDelegate extends DelegateBase {
if ( launch ) {
GameUtils.launchGame( m_activity, m_newRowID, networked );
if ( networked ) {
GameUtils.launchInviteActivity( m_activity, inviteHow, room,
inviteID, lang[0], dict[0],
nPlayers );
Assert.fail();
// GameUtils.launchInviteActivity( m_activity, inviteHow, room,
// inviteID, lang[0], dict[0],
// nPlayers );
}
finish();
} else {

View file

@ -1263,7 +1263,7 @@ public class RelayService extends XWService
try {
m_packetID = nextPacketID( m_cmd );
DataOutputStream out = new DataOutputStream( bas );
DbgUtils.logf( "Building packet with cmd %s",
DbgUtils.logf( "RelayService.makeHeader(): building packet with cmd %s",
m_cmd.toString() );
out.writeByte( XWPDevProto.XWPDEV_PROTO_VERSION_1.ordinal() );
un2vli( m_packetID, out );

View file

@ -24,7 +24,6 @@ import java.net.InetAddress;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import junit.framework.Assert;
@ -98,10 +97,7 @@ public class CommsAddrRec {
public CommsAddrRec( String host, int port )
{
this( CommsConnType.COMMS_CONN_RELAY );
ip_relay_hostName = host;
ip_relay_port = port;
ip_relay_seeksPublicRoom = false;
ip_relay_advertiseRoom = false;
setRelayParams( host, port );
}
public CommsAddrRec( String btHost, String btAddr )
@ -122,6 +118,20 @@ public class CommsAddrRec {
this.copyFrom( src );
}
public void setRelayParams( String host, int port, String room )
{
setRelayParams( host, port );
ip_relay_invite = room;
}
public void setRelayParams( String host, int port )
{
ip_relay_hostName = host;
ip_relay_port = port;
ip_relay_seeksPublicRoom = false;
ip_relay_advertiseRoom = false;
}
public boolean changesMatter( final CommsAddrRec other )
{
boolean matter = ! conTypes.equals( other.conTypes );

View file

@ -61,7 +61,7 @@ public class GameSummary {
public int pendingMsgLevel;
public long modtime;
public int gameID;
public String[] remoteDevs; // BTAddr or phone number
public String[] remoteDevs; // BTAddrs and phone numbers
public int dictLang;
public DeviceRole serverRole;

View file

@ -390,9 +390,9 @@ public class JNIThread extends Thread {
break;
case CMD_RECEIVE:
CommsAddrRec ret = (CommsAddrRec)args[1];
draw = XwJNI.game_receiveMessage( m_jniGamePtr,
(byte[])args[0],
(CommsAddrRec)args[1]);
(byte[])args[0], ret );
handle( JNICmd.CMD_DO );
if ( draw ) {
handle( JNICmd.CMD_SAVE );

View file

@ -1707,7 +1707,7 @@ preProcess( CommsCtxt* comms, XWStreamCtxt* stream,
case COMMS_CONN_RELAY:
consumed = relayPreProcess( comms, stream, senderID );
if ( !consumed ) {
*usingRelay = addr_hasType( &comms->addr, COMMS_CONN_RELAY );
*usingRelay = XP_TRUE;
}
break;
#endif
@ -1919,7 +1919,8 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
XWHostID senderID = 0; /* unset; default for non-relay cases */
XP_Bool usingRelay = XP_FALSE;
XP_ASSERT( retAddr == NULL || addr_getType( &comms->addr ) == addr_getType( retAddr ) );
XP_ASSERT( retAddr == NULL ||
(0 != (comms->addr._conTypes & retAddr->_conTypes)) );
#ifdef COMMS_CHECKSUM
XP_U16 initialLen = stream_getSize( stream );
#endif
@ -2024,7 +2025,9 @@ XP_Bool
comms_isConnected( const CommsCtxt* const comms )
{
XP_Bool result = XP_FALSE;
switch ( addr_getType( &comms->addr ) ) {
CommsConnType typ;
for ( XP_U32 st = 0; !result && addr_iter( &comms->addr, &typ, &st ); ) {
switch ( typ ) {
case COMMS_CONN_RELAY:
result = 0 != comms->rr.connName[0];
break;
@ -2034,6 +2037,7 @@ comms_isConnected( const CommsCtxt* const comms )
default:
break;
}
}
return result;
}