Merge branch 'android_branch' into android_groups

Conflicts:
	xwords4/android/XWords4/src/org/eehouse/android/xw4/GamesList.java
This commit is contained in:
Eric House 2012-11-25 14:11:04 -08:00
commit ac435b7857
14 changed files with 311 additions and 87 deletions

View file

@ -61,6 +61,7 @@
</exec>
<exec dir="." executable="../scripts/gen_gcmid.sh"
output="src/org/eehouse/android/xw4/GCMConsts.java"
logError="true"
/>
<exec dir=".." executable="./scripts/genvers.sh" output="ant_out.txt">
<arg value="XWords4"/>

View file

@ -2127,6 +2127,10 @@
play Crosswords using the wordlist %2$s (for play in %3$s), but it
is not installed. Would you like to download the wordlist or
decline the invitation?</string>
<string name="invite_dict_missing_body_nonamef">You have been invited to
play Crosswords using the wordlist %2$s (for play in %3$s), but it
is not installed. Would you like to download the wordlist or
decline the invitation?</string>
<string name="button_decline">Decline</string>
<string name="downloadingf">Downloading %s...</string>

View file

@ -816,9 +816,8 @@ public class BoardActivity extends XWActivity
if ( DlgDelegate.DISMISS_BUTTON != which ) {
GameUtils.launchInviteActivity( BoardActivity.this,
DlgDelegate.EMAIL_BTN == which,
m_room, null,
m_gi.dictLang,
m_gi.nPlayers );
m_room, null, m_gi.dictLang,
m_gi.dictName, m_gi.nPlayers );
}
} else if ( AlertDialog.BUTTON_POSITIVE == which ) {
JNICmd cmd = JNICmd.CMD_NONE;

View file

@ -547,7 +547,7 @@ public class DBUtils {
DBHelper.ROOMNAME, nli.room,
DBHelper.INVITEID, nli.inviteID,
DBHelper.DICTLANG, nli.lang,
DBHelper.NUM_PLAYERS, nli.nPlayers );
DBHelper.NUM_PLAYERS, nli.nPlayersT );
Cursor cursor = db.query( DBHelper.TABLE_NAME_SUM, columns,
selection, null, null, null, null );
if ( 1 == cursor.getCount() && cursor.moveToFirst() ) {
@ -563,7 +563,7 @@ public class DBUtils {
{
long rowid = ROWID_NOTFOUND;
NetLaunchInfo nli = new NetLaunchInfo( data );
if ( null != nli ) {
if ( null != nli && nli.isValid() ) {
rowid = getRowIDForOpen( context, nli );
}
return rowid;

View file

@ -64,11 +64,30 @@ public class DispatchNotify extends Activity {
if ( !tryHandle( gameID ) ) {
mustLaunch = true;
}
} else if ( null != data ) {
long rowid = DBUtils.getRowIDForOpen( this, data );
} else if ( null != data ) { // relay invite redirected URL case
NetLaunchInfo nli = new NetLaunchInfo( data );
long rowid = DBUtils.getRowIDForOpen( this, nli );
if ( DBUtils.ROWID_NOTFOUND == rowid ) {
if ( !tryHandle( data ) ) {
mustLaunch = true;
boolean haveDict;
if ( null == nli.dict ) { // can only test for language support
haveDict =
0 < DictLangCache.getHaveLang( this, nli.lang ).length;
} else {
haveDict = DictLangCache.haveDict( this, nli.lang, nli.dict );
}
if ( haveDict ) {
if ( !tryHandle( data ) ) {
mustLaunch = true;
}
} else {
Intent intent = MultiService.makeMissingDictIntent( this,
nli );
intent.putExtra( MultiService.OWNER,
MultiService.OWNER_RELAY );
// do we have gameID?
MultiService.
postMissingDictNotification( this, intent,
nli.inviteID.hashCode() );
}
} else {
DbgUtils.logf( "DispatchNotify: dropping duplicate invite" );

View file

@ -74,7 +74,7 @@ public class GCMIntentService extends GCMBaseIntentService {
public static void init( Application app )
{
int sdkVersion = Integer.valueOf( android.os.Build.VERSION.SDK );
if ( 8 <= sdkVersion ) {
if ( 8 <= sdkVersion && 0 < GCMConsts.SENDER_ID.length() ) {
try {
GCMRegistrar.checkDevice( app );
// GCMRegistrar.checkManifest( app );

View file

@ -24,15 +24,8 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import android.content.res.AssetManager;
import java.util.concurrent.locks.Lock;
import java.util.HashMap;
import java.util.HashSet;
@ -432,7 +425,7 @@ public class GameUtils {
}
private static long makeNewMultiGame( Context context, CommsAddrRec addr,
int[] lang, String dict,
int[] lang, String[] dict,
int nPlayersT, int nPlayersH,
String inviteID, int gameID,
boolean isHost )
@ -440,8 +433,9 @@ public class GameUtils {
long rowid = -1;
CurGameInfo gi = new CurGameInfo( context, true );
gi.setLang( lang[0], dict );
gi.setLang( lang[0], dict[0] );
lang[0] = gi.dictLang;
dict[0] = gi.dictName;
gi.setNPlayers( nPlayersT, nPlayersH );
gi.juggle();
if ( 0 != gameID ) {
@ -466,7 +460,8 @@ public class GameUtils {
public static long makeNewNetGame( Context context, String room,
String inviteID, int[] lang,
int nPlayersT, int nPlayersH )
String[] dict, int nPlayersT,
int nPlayersH )
{
long rowid = -1;
String relayName = XWPrefs.getDefaultRelayHost( context );
@ -474,21 +469,24 @@ public class GameUtils {
CommsAddrRec addr = new CommsAddrRec( relayName, relayPort );
addr.ip_relay_invite = room;
return makeNewMultiGame( context, addr, lang, null, nPlayersT,
return makeNewMultiGame( context, addr, lang, dict, nPlayersT,
nPlayersH, inviteID, 0, false );
}
public static long makeNewNetGame( Context context, String room,
String inviteID, int lang, int nPlayers )
String inviteID, int lang, String dict,
int nPlayers )
{
int[] langarr = { lang };
return makeNewNetGame( context, room, inviteID, langarr, nPlayers, 1 );
String[] dictArr = { dict };
return makeNewNetGame( context, room, inviteID, langarr, dictArr,
nPlayers, 1 );
}
public static long makeNewNetGame( Context context, NetLaunchInfo info )
{
return makeNewNetGame( context, info.room, info.inviteID, info.lang,
info.nPlayers );
info.dict, info.nPlayersT );
}
public static long makeNewBTGame( Context context, int gameID,
@ -512,11 +510,12 @@ public class GameUtils {
{
long rowid = -1;
int[] langa = { lang };
String[] dicta = { dict };
boolean isHost = null == addr;
if ( isHost ) {
addr = new CommsAddrRec(CommsAddrRec.CommsConnType.COMMS_CONN_SMS);
}
return makeNewMultiGame( context, addr, langa, dict, nPlayersT,
return makeNewMultiGame( context, addr, langa, dicta, nPlayersT,
nPlayersH, null, gameID, isHost );
}
@ -539,13 +538,14 @@ public class GameUtils {
public static void launchInviteActivity( Context context,
boolean choseEmail,
String room, String inviteID,
int lang, int nPlayers )
int lang, String dict,
int nPlayers )
{
if ( null == inviteID ) {
inviteID = makeRandomID();
}
Uri gameUri = NetLaunchInfo.makeLaunchUri( context, room, inviteID,
lang, nPlayers );
lang, dict, nPlayers );
if ( null != gameUri ) {
int fmtId = choseEmail? R.string.invite_htmf : R.string.invite_txtf;

View file

@ -88,7 +88,7 @@ public class GamesList extends XWExpandableListActivity
private GameListAdapter m_adapter;
private String m_missingDict;
private String[] m_missingDictNames;
private String m_missingDictName;
private long m_missingDictRowId;
private String[] m_sameLangDicts;
private int m_missingDictLang;
@ -115,10 +115,15 @@ public class GamesList extends XWExpandableListActivity
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
// just do one
NetUtils.downloadDictInBack( GamesList.this,
m_missingDictLang,
m_missingDictNames[0],
GamesList.this );
if ( null == m_missingDictName ) {
DictsActivity.launchAndDownload( GamesList.this,
m_missingDictLang );
} else {
NetUtils.downloadDictInBack( GamesList.this,
m_missingDictLang,
m_missingDictName,
GamesList.this );
}
}
};
String message;
@ -130,7 +135,7 @@ public class GamesList extends XWExpandableListActivity
gameName, langName );
} else {
message = getString( R.string.no_dict_substf,
gameName, m_missingDictNames[0],
gameName, m_missingDictName,
langName );
}
@ -163,8 +168,10 @@ public class GamesList extends XWExpandableListActivity
dict = DictLangCache.stripCount( dict );
GameUtils.replaceDicts( GamesList.this,
m_missingDictRowId,
m_missingDictNames[0],
m_missingDictName,
dict );
GameUtils.launchGame( GamesList.this,
m_missingDictRowId );
}
};
dialog = new AlertDialog.Builder( this )
@ -404,7 +411,7 @@ public class GamesList extends XWExpandableListActivity
super.onSaveInstanceState( outState );
outState.putLong( SAVE_ROWID, m_rowid );
outState.putLong( SAVE_GROUPID, m_groupid );
outState.putStringArray( SAVE_DICTNAMES, m_missingDictNames );
outState.putString( SAVE_DICTNAMES, m_missingDictName );
if ( null != m_netLaunchInfo ) {
m_netLaunchInfo.putSelf( outState );
}
@ -416,7 +423,7 @@ public class GamesList extends XWExpandableListActivity
m_rowid = bundle.getLong( SAVE_ROWID );
m_groupid = bundle.getLong( SAVE_GROUPID );
m_netLaunchInfo = new NetLaunchInfo( bundle );
m_missingDictNames = bundle.getStringArray( SAVE_DICTNAMES );
m_missingDictName = bundle.getString( SAVE_DICTNAMES );
}
}
@ -834,13 +841,21 @@ public class GamesList extends XWExpandableListActivity
missingNames,
missingLang );
if ( !hasDicts ) {
m_missingDictNames = missingNames[0];
m_missingDictLang = missingLang[0];
if ( 0 < missingNames[0].length ) {
m_missingDictName = missingNames[0][0];
} else {
m_missingDictName = null;
}
m_missingDictRowId = rowid;
if ( 0 == DictLangCache.getLangCount( this, m_missingDictLang ) ) {
showDialog( WARN_NODICT );
} else {
} else if ( null != m_missingDictName ) {
showDialog( WARN_NODICT_SUBST );
} else {
String dict = DictLangCache.getHaveLang( this, m_missingDictLang)[0];
GameUtils.replaceDicts( this, m_missingDictRowId, null, dict );
GameUtils.launchGame( this, m_missingDictRowId );
}
}
return hasDicts;
@ -911,13 +926,18 @@ public class GamesList extends XWExpandableListActivity
private void startNewNetGame( Intent intent )
{
Uri data = intent.getData();
if ( null != data ) {
NetLaunchInfo info = new NetLaunchInfo( data );
if ( info.isValid() ) {
startNewNetGame( info );
NetLaunchInfo info = null;
if ( MultiService.isMissingDictIntent( intent ) ) {
info = new NetLaunchInfo( intent );
} else {
Uri data = intent.getData();
if ( null != data ) {
info = new NetLaunchInfo( data );
}
}
if ( null != info && info.isValid() ) {
startNewNetGame( info );
}
} // startNewNetGame
private void startHasGameID( int gameID )
@ -972,4 +992,9 @@ public class GamesList extends XWExpandableListActivity
return dialog;
}
public static void onGameDictDownload( Context context, Intent intent )
{
intent.setClass( context, GamesList.class );
context.startActivity( intent );
}
}

View file

@ -31,6 +31,8 @@ public class MultiService {
public static final String LANG = "LANG";
public static final String DICT = "DICT";
public static final String GAMEID = "GAMEID";
public static final String INVITEID = "INVITEID"; // relay only
public static final String ROOM = "ROOM";
public static final String GAMENAME = "GAMENAME";
public static final String NPLAYERST = "NPLAYERST";
public static final String NPLAYERSH = "NPLAYERSH";
@ -38,6 +40,7 @@ public class MultiService {
public static final String OWNER = "OWNER";
public static final int OWNER_SMS = 1;
public static final int OWNER_RELAY = 2;
private BTEventListener m_li;
@ -84,11 +87,39 @@ public class MultiService {
}
}
public static void fillInviteIntent( Intent intent, String gameName,
int lang, String dict,
int nPlayersT, int nPlayersH )
{
intent.putExtra( GAMENAME, gameName );
intent.putExtra( LANG, lang );
intent.putExtra( DICT, dict );
intent.putExtra( NPLAYERST, nPlayersT ); // both of these used
intent.putExtra( NPLAYERSH, nPlayersH );
}
public static Intent makeMissingDictIntent( Context context, String gameName,
int lang, String dict,
int nPlayersT, int nPlayersH )
{
Intent intent = new Intent( context, DictsActivity.class );
fillInviteIntent( intent, gameName, lang, dict, nPlayersT, nPlayersH );
return intent;
}
public static Intent makeMissingDictIntent( Context context, NetLaunchInfo nli )
{
Intent intent = makeMissingDictIntent( context, null, nli.lang, nli.dict,
nli.nPlayersT, 1 );
intent.putExtra( ROOM, nli.room );
return intent;
}
public static boolean isMissingDictIntent( Intent intent )
{
return intent.hasExtra( LANG )
&& intent.hasExtra( DICT )
&& intent.hasExtra( GAMEID )
// && intent.hasExtra( DICT )
&& (intent.hasExtra( GAMEID ) || intent.hasExtra( ROOM ))
&& intent.hasExtra( GAMENAME )
&& intent.hasExtra( NPLAYERST )
&& intent.hasExtra( NPLAYERSH );
@ -102,8 +133,10 @@ public class MultiService {
String langStr = DictLangCache.getLangName( context, lang );
String dict = intent.getStringExtra( DICT );
String inviter = intent.getStringExtra( INVITER );
String msg = context.getString( R.string.invite_dict_missing_bodyf,
inviter, dict, langStr );
int msgID = (null == inviter) ? R.string.invite_dict_missing_body_nonamef
: R.string.invite_dict_missing_bodyf;
String msg = context.getString( msgID, inviter, dict, langStr );
return new AlertDialog.Builder( context )
.setTitle( R.string.invite_dict_missing_title )
.setMessage( msg)
@ -112,6 +145,13 @@ public class MultiService {
.create();
}
public static void postMissingDictNotification( Context content,
Intent intent, int id )
{
Utils.postNotification( content, intent, R.string.missing_dict_title,
R.string.missing_dict_detail, id );
}
// resend the intent, but only if the dict it names is here. (If
// it's not, we may need to try again later, e.g. because our cue
// was a focus gain.)
@ -123,11 +163,15 @@ public class MultiService {
String dict = intent.getStringExtra( DICT );
downloaded = DictLangCache.haveDict( context, lang, dict );
if ( downloaded ) {
int owner = intent.getIntExtra( OWNER, -1 );
if ( owner == OWNER_SMS ) {
switch ( intent.getIntExtra( OWNER, -1 ) ) {
case OWNER_SMS:
SMSService.onGameDictDownload( context, intent );
} else {
DbgUtils.logf( "unexpected OWNER: %d", owner );
break;
case OWNER_RELAY:
GamesList.onGameDictDownload( context, intent );
break;
default:
DbgUtils.logf( "unexpected OWNER" );
}
}
}

View file

@ -21,8 +21,9 @@
package org.eehouse.android.xw4;
import android.content.Context;
import android.net.Uri;
import android.content.Intent;
import android.net.Uri.Builder;
import android.net.Uri;
import android.os.Bundle;
import java.net.URLEncoder;
@ -30,11 +31,13 @@ import java.net.URLEncoder;
public class NetLaunchInfo {
public String room;
public String inviteID;
public String dict;
public int lang;
public int nPlayers;
public int nPlayersT;
private static final String LANG = "netlaunchinfo_lang";
private static final String ROOM = "netlaunchinfo_room";
private static final String DICT = "netlaunchinfo_dict";
private static final String INVITEID = "netlaunchinfo_inviteid";
private static final String NPLAYERS = "netlaunchinfo_nplayers";
private static final String VALID = "netlaunchinfo_valid";
@ -46,7 +49,8 @@ public class NetLaunchInfo {
bundle.putInt( LANG, lang );
bundle.putString( ROOM, room );
bundle.putString( INVITEID, inviteID );
bundle.putInt( NPLAYERS, nPlayers );
bundle.putString( DICT, dict );
bundle.putInt( NPLAYERS, nPlayersT );
bundle.putBoolean( VALID, m_valid );
}
@ -54,8 +58,9 @@ public class NetLaunchInfo {
{
lang = bundle.getInt( LANG );
room = bundle.getString( ROOM );
dict = bundle.getString( DICT );
inviteID = bundle.getString( INVITEID );
nPlayers = bundle.getInt( NPLAYERS );
nPlayersT = bundle.getInt( NPLAYERS );
m_valid = bundle.getBoolean( VALID );
}
@ -66,10 +71,11 @@ public class NetLaunchInfo {
try {
room = data.getQueryParameter( "room" );
inviteID = data.getQueryParameter( "id" );
dict = data.getQueryParameter( "wl" );
String langStr = data.getQueryParameter( "lang" );
lang = Integer.decode( langStr );
String np = data.getQueryParameter( "np" );
nPlayers = Integer.decode( np );
nPlayersT = Integer.decode( np );
m_valid = true;
} catch ( Exception e ) {
DbgUtils.logf( "unable to parse \"%s\"", data.toString() );
@ -77,8 +83,21 @@ public class NetLaunchInfo {
}
}
public NetLaunchInfo( Intent intent )
{
room = intent.getStringExtra( MultiService.ROOM );
inviteID = intent.getStringExtra( MultiService.INVITEID );
lang = intent.getIntExtra( MultiService.LANG, -1 );
dict = intent.getStringExtra( MultiService.DICT );
nPlayersT = intent.getIntExtra( MultiService.NPLAYERST, -1 );
m_valid = null != room
&& -1 != lang
&& -1 != nPlayersT;
}
public static Uri makeLaunchUri( Context context, String room,
String inviteID, int lang, int nPlayers )
String inviteID, int lang,
String dict, int nPlayersT )
{
Builder ub = new Builder();
ub.scheme( "http" );
@ -86,9 +105,12 @@ public class NetLaunchInfo {
XWPrefs.getDefaultRedirHost( context ) ) );
ub.appendQueryParameter( "lang", String.format("%d", lang ) );
ub.appendQueryParameter( "np", String.format( "%d", nPlayers ) );
ub.appendQueryParameter( "np", String.format( "%d", nPlayersT ) );
ub.appendQueryParameter( "room", room );
ub.appendQueryParameter( "id", inviteID );
if ( null != dict ) {
ub.appendQueryParameter( "wl", dict );
}
return ub.build();
}

View file

@ -318,13 +318,14 @@ public class NewGameActivity extends XWActivity {
String inviteID = null;
long rowid;
int[] lang = {0};
String[] dict = {null};
final int nPlayers = 2; // hard-coded for no-configure case
if ( networked ) {
room = GameUtils.makeRandomID();
inviteID = GameUtils.makeRandomID();
rowid = GameUtils.makeNewNetGame( this, room, inviteID, lang,
nPlayers, 1 );
dict, nPlayers, 1 );
} else {
rowid = GameUtils.saveNew( this, new CurGameInfo( this ) );
}
@ -333,7 +334,8 @@ public class NewGameActivity extends XWActivity {
GameUtils.launchGame( this, rowid, networked );
if ( networked ) {
GameUtils.launchInviteActivity( this, choseEmail, room,
inviteID, lang[0], nPlayers );
inviteID, lang[0], dict[0],
nPlayers );
}
} else {
GameUtils.doConfig( this, rowid, GameConfig.class );

View file

@ -424,17 +424,15 @@ public class SMSService extends Service {
makeForInvite( phone, gameID, gameName, lang, dict,
nPlayersT, nPlayersH );
} else {
Intent intent = new Intent( this, DictsActivity.class );
fillInviteIntent( intent, phone, gameID, gameName, lang, dict,
nPlayersT, nPlayersH );
Intent intent = MultiService
.makeMissingDictIntent( this, gameName, lang, dict,
nPlayersT, nPlayersH );
intent.putExtra( PHONE, phone );
intent.putExtra( MultiService.OWNER,
MultiService.OWNER_SMS );
intent.putExtra( MultiService.INVITER,
Utils.phoneToContact( this, phone, true ) );
Utils.postNotification( this, intent,
R.string.missing_dict_title,
R.string.missing_dict_detail,
gameID );
MultiService.postMissingDictNotification( this, intent, gameID );
}
break;
case DATA:
@ -591,11 +589,8 @@ public class SMSService extends Service {
{
intent.putExtra( PHONE, phone );
intent.putExtra( MultiService.GAMEID, gameID );
intent.putExtra( MultiService.GAMENAME, gameName );
intent.putExtra( MultiService.LANG, lang );
intent.putExtra( MultiService.DICT, dict );
intent.putExtra( MultiService.NPLAYERST, nPlayersT );
intent.putExtra( MultiService.NPLAYERSH, nPlayersH );
MultiService.fillInviteIntent( intent, gameName, lang, dict,
nPlayersT, nPlayersH );
}
private void feedMessage( int gameID, byte[] msg, CommsAddrRec addr )

View file

@ -1,8 +1,11 @@
#!/bin/sh
set -e -u
GCM_SENDER_ID=${GCM_SENDER_ID:-""}
if [ -z "$GCM_SENDER_ID" ]; then
echo "GCM_SENDER_ID not in env"
exit 1
echo "GCM_SENDER_ID empty; GCM use will be disabled" >&2
fi
cat <<EOF

View file

@ -1,8 +1,32 @@
<!-- -*- mode: sgml; -*- -->
<?php
// script to work around URLs with custom schemes not being clickable in
// Android's SMS app. It runs on my server and SMS messages hold links to it
// that it then redirects to the passed-in scheme.
function langToString( $code ) {
switch ( $code ) {
case 1: return "English";
case 2: return "French";
case 3: return "German";
case 4: return "Turkish";
case 5: return "Arabic";
case 6: return "Spanish";
case 7: return "Swedish";
case 8: return "Polish";
case 9: return "Danish";
case 0xA: return "Italian";
case 0xB: return "Dutch";
case 0xC: return "Catalan";
case 0xD: return "Portuguese";
case 0XF: return "Russian";
case 0x11: return "Czech";
case 0x12: return "Greek";
case 0x13: return "Slovak";
default:
return "<unknown>";
}
}
$g_androidStrings = array( "android", );
$scheme = "newxwgame";
$host = "10.0.2.2";
@ -10,33 +34,46 @@ $lang = $_REQUEST["lang"];
$room = $_REQUEST["room"];
$np = $_REQUEST["np"];
$id = $_REQUEST["id"];
$wl = $_REQUEST["wl"];
$content = "0; url=$scheme://$host?room=$room&lang=$lang&np=$np";
$agent = $_SERVER['HTTP_USER_AGENT'];
$onAndroid = false;
for ( $ii = 0; $ii < count($g_androidStrings) && !$onAndroid; ++$ii ) {
$needle = $g_androidStrings[$ii];
$onAndroid = 0 != stripos( $agent, $needle );
}
$onFire = 0 != stripos( $agent, 'silk' );
$localurl = "$scheme://$host?room=$room&lang=$lang&np=$np";
if ( $id != "" ) {
$content .= "&id=$id";
$localurl .= "&id=$id";
}
if ( $wl != "" ) {
$localurl .= "&wl=$wl";
}
if ( $onAndroid || $onFire ) {
print <<<EOF
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<link rel="stylesheet" type="text/css" href="/xw4mobile.css" />
<title>Crosswords SMS redirect</title>
<meta http-equiv="REFRESH"
content="$content">
<title>Crosswords Invite redirect</title>
</head>
<body>
<div align="center">
<img src="./icon48x48.png">
<img src="./icon48x48.png"/>
<p>redirecting to Crosswords....</p>
<p>This page is meant to be viewed (briefly) on your Android device after which Crosswords should launch.
If this fails it's probably because you don't have a new enough version of Crosswords installed.
<h1><a href="$localurl">Tap this link to launch Crosswords with
your new game.</a>
</h1>
<p>If this fails it&apos;s probably because you don&apos;t have a new enough
version of Crosswords installed.
</p>
<img src="./icon48x48.png"/>
</div>
</body>
@ -44,4 +81,77 @@ print <<<EOF
EOF;
} else if ( $onFire ) {
$langString = langToString($lang);
$langText = "Make sure the language chosen is $langString";
if ( '' != $wl ) {
$langText .= " and the wordlist is $wl.";
}
$langText .= " If you don't have a[n] $langString wordlist installed you'll need to do that first.";
print <<<EOF
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Crosswords Invite redirect</title>
</head>
<body>
<p>It appears you&apos;re running on a Kindle Fire, whose non-standard (from
an Android perspective) OS doesn't support the custom schemes on which
Crosswords invitations depend. If you want to accept this invitation
you'll need to do it the manual way:
<ol>
<li>Open Crosswords, and navigate to the main Games List screen</li>
<li>Choose &quot;Add game&quot;, either from the menu or the button at the bottom.</li>
<li>Under &quot;New Networked game&quot;, choose &quot;Configure first&quot;.</li>
<li>$langText</li>
<li>As the room name, enter &quot;$room&quot;.</li>
<li>Make sure the total number of players shown is $np and that only one of them is not an &quot;Off-device player&quot;.</li>
<li>Now tap the &quot;Play game&quot; button at the bottom (above the keyboard). Your new game should open and connect.</li>
</ol></p>
<p>I&apos;m sorry this is so complicated. I&apos;m trying to find a
workaround for this limitation in the Kindle Fire's operating system
but for now this is all I can offer.</p>
<p>(Just in case Amazon&apos;s fixed the
problem, <a href="$localurl">here is the link</a> that should open
your new game.)</p>
</body>
</html>
EOF;
} else {
$subject = "Android device not identified";
$body = htmlentities("My browser is running on an android device but"
. " says its user agent is: \"$agent\". Please fix your script to recognize"
. " this as an Android browser.");
print <<<EOF
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Crosswords Invite redirect</title>
</head>
<body>
<div align="center">
<img src="./icon48x48.png"/>
</div>
<p>This page is meant to be viewed on a browser on your Android
device. Please open the email that sent you here on that device and
revisit this link to complete the invitation process.
</p>
<p>(If you <em>are</em> viewing this on an Android device, you've
found a bug! Please <a href="mailto:
xwords@eehouse.org?subject=$subject&body=$body">email me</a> (and be
sure to leave the user agent string in the email body.)
</p>
</body>
</html>
EOF;
}
?>