Merge branch 'android_branch' into gtk_multigame

Conflicts:
	xwords4/android/XWords4/src/org/eehouse/android/xw4/GCMIntentService.java
	xwords4/android/XWords4/src/org/eehouse/android/xw4/RelayService.java
This commit is contained in:
Eric House 2013-05-22 07:01:08 -07:00
commit 31c443e4b1
7 changed files with 221 additions and 74 deletions

View file

@ -36,7 +36,7 @@ local_DEFINES += \
-DHASH_STREAM \
-DXWFEATURE_BASE64 \
-DXWFEATURE_DEVID \
-DINITIAL_CLIENT_VERS=2 \
-DINITIAL_CLIENT_VERS=3 \
-DRELAY_ROOM_DEFAULT=\"\" \
-D__LITTLE_ENDIAN \

View file

@ -25,6 +25,7 @@ import android.content.Context;
import android.content.Intent;
import com.google.android.gcm.GCMBaseIntentService;
import com.google.android.gcm.GCMRegistrar;
import org.json.JSONArray;
public class GCMIntentService extends GCMBaseIntentService {
@ -61,13 +62,31 @@ public class GCMIntentService extends GCMBaseIntentService {
if ( ignoreIt ) {
DbgUtils.logf( "received GCM but ignoring it" );
} else {
value = intent.getStringExtra( "checkUpdates" );
if ( null != value && Boolean.parseBoolean( value ) ) {
UpdateCheckReceiver.checkVersions( context, true );
}
value = intent.getStringExtra( "getMoves" );
if ( null != value && Boolean.parseBoolean( value ) ) {
RelayReceiver.RestartTimer( context, true );
}
value = intent.getStringExtra( "checkUpdates" );
if ( null != value && Boolean.parseBoolean( value ) ) {
UpdateCheckReceiver.checkVersions( context, true );
value = intent.getStringExtra( "msgs64" );
if ( null != value ) {
String connname = intent.getStringExtra( "connname" );
if ( null != connname ) {
try {
JSONArray msgs64 = new JSONArray( value );
String[] strs64 = new String[msgs64.length()];
for ( int ii = 0; ii < strs64.length; ++ii ) {
strs64[ii] = msgs64.optString(ii);
}
RelayService.processMsgs( context, connname, strs64 );
} catch (org.json.JSONException jse ) {
DbgUtils.loge( jse );
}
}
}
value = intent.getStringExtra( "msg" );
@ -76,6 +95,7 @@ public class GCMIntentService extends GCMBaseIntentService {
if ( null != title ) {
int code = value.hashCode() ^ title.hashCode();
Utils.postNotification( context, null, title, value, code );
}
}
}

View file

@ -23,7 +23,12 @@ package org.eehouse.android.xw4;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
<<<<<<< HEAD
import java.io.ByteArrayInputStream;
=======
import android.os.AsyncTask;
import android.os.IBinder;
>>>>>>> android_branch
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@ -43,16 +48,21 @@ import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.GameSummary;
import org.eehouse.android.xw4.jni.UtilCtxt;
import org.eehouse.android.xw4.jni.XwJNI;
public class RelayService extends XWService {
private static final int MAX_SEND = 1024;
private static final int MAX_BUF = MAX_SEND - 2;
private static final String CMD_STR = "CMD";
private static final int UDP_CHANGED = 1;
private static final int SEND = 2;
private static final int RECEIVE = 3;
private static final int PROCESS_MSGS = 1;
private static final int UDP_CHANGED = 2;
private static final int SEND = 3;
private static final int RECEIVE = 4;
private static final String MSGS = "MSGS";
private static final String RELAY_ID = "RELAY_ID";
private static final String ROWID = "ROWID";
private static final String BINBUFFER = "BINBUFFER";
@ -103,9 +113,9 @@ public class RelayService extends XWService {
public static int sendPacket( Context context, long rowid, byte[] buf )
{
Intent intent = getIntentTo( context, SEND );
intent.putExtra( ROWID, rowid );
intent.putExtra( BINBUFFER, buf );
Intent intent = getIntentTo( context, SEND )
.putExtra( ROWID, rowid )
.putExtra( BINBUFFER, buf );
context.startService( intent );
return buf.length;
}
@ -115,9 +125,9 @@ public class RelayService extends XWService {
{
DbgUtils.logf( "RelayService::postData: packet of length %d for token %d",
msg.length, rowid );
Intent intent = getIntentTo( context, RECEIVE );
intent.putExtra( ROWID, rowid );
intent.putExtra( BINBUFFER, msg );
Intent intent = getIntentTo( context, RECEIVE )
.putExtra( ROWID, rowid )
.putExtra( BINBUFFER, msg );
context.startService( intent );
}
@ -126,6 +136,15 @@ public class RelayService extends XWService {
startService( context );
}
public static void processMsgs( Context context, String relayId,
String[] msgs64 )
{
Intent intent = getIntentTo( context, PROCESS_MSGS )
.putExtra( MSGS, msgs64 )
.putExtra( RELAY_ID, relayId );
context.startService( intent );
}
private static Intent getIntentTo( Context context, int cmd )
{
Intent intent = new Intent( context, RelayService.class );
@ -183,6 +202,31 @@ public class RelayService extends XWService {
return result;
}
@Override
public int onStartCommand( Intent intent, int flags, int startId )
{
int cmd = intent.getIntExtra( CMD_STR, -1 );
switch( cmd ) {
case PROCESS_MSGS:
String[] relayIDs = new String[1];
relayIDs[0] = intent.getStringExtra( RELAY_ID );
long[] rowIDs = DBUtils.getRowIDsFor( this, relayIDs[0] );
if ( 0 < rowIDs.length ) {
String[] msgs64 = intent.getStringArrayExtra( MSGS );
int count = msgs64.length;
byte[][][] msgs = new byte[1][count][];
for ( int ii = 0; ii < count; ++ii ) {
msgs[0][ii] = XwJNI.base64Decode( msgs64[ii] );
}
process( msgs, rowIDs, relayIDs );
}
break;
}
stopSelf( startId );
return Service.START_NOT_STICKY;
}
private void setupNotification( String[] relayIDs )
{
for ( String relayID : relayIDs ) {
@ -568,67 +612,80 @@ public class RelayService extends XWService {
long[][] rowIDss = new long[1][];
String[] relayIDs = DBUtils.getRelayIDs( this, rowIDss );
if ( null != relayIDs && 0 < relayIDs.length ) {
long[] rowIDs = rowIDss[0];
byte[][][] msgs = NetUtils.queryRelay( this, relayIDs );
if ( null != msgs ) {
RelayMsgSink sink = new RelayMsgSink();
int nameCount = relayIDs.length;
ArrayList<String> idsWMsgs =
new ArrayList<String>( nameCount );
for ( int ii = 0; ii < nameCount; ++ii ) {
byte[][] forOne = msgs[ii];
// if game has messages, open it and feed 'em
// to it.
if ( null == forOne ) {
// Nothing for this relayID
} else if ( BoardActivity.feedMessages( rowIDs[ii], forOne )
|| GameUtils.feedMessages( this, rowIDs[ii],
forOne, null,
sink ) ) {
idsWMsgs.add( relayIDs[ii] );
} else {
DbgUtils.logf( "dropping message for %s (rowid %d)",
relayIDs[ii], rowIDs[ii] );
}
}
if ( 0 < idsWMsgs.size() ) {
String[] tmp = new String[idsWMsgs.size()];
idsWMsgs.toArray( tmp );
setupNotification( tmp );
}
sink.send( this );
}
process( msgs, rowIDss[0], relayIDs );
}
}
private static void sendToRelay( Context context,
HashMap<String,ArrayList<byte[]>> msgHash )
private void process( byte[][][] msgs, long[] rowIDs, String[] relayIDs )
{
// format: total msg lenth: 2
// number-of-relayIDs: 2
// for-each-relayid: relayid + '\n': varies
// message count: 1
// for-each-message: length: 2
// message: varies
if ( null != msgs ) {
RelayMsgSink sink = new RelayMsgSink();
int nameCount = relayIDs.length;
ArrayList<String> idsWMsgs = new ArrayList<String>( nameCount );
if ( null != msgHash ) {
for ( int ii = 0; ii < nameCount; ++ii ) {
byte[][] forOne = msgs[ii];
// if game has messages, open it and feed 'em to it.
if ( null == forOne ) {
// Nothing for this relayID
} else if ( BoardActivity.feedMessages( rowIDs[ii], forOne )
|| GameUtils.feedMessages( this, rowIDs[ii],
forOne, null,
sink ) ) {
idsWMsgs.add( relayIDs[ii] );
} else {
DbgUtils.logf( "message for %s (rowid %d) not consumed",
relayIDs[ii], rowIDs[ii] );
}
}
if ( 0 < idsWMsgs.size() ) {
String[] tmp = new String[idsWMsgs.size()];
idsWMsgs.toArray( tmp );
setupNotification( tmp );
}
sink.send( this );
}
}
private static class AsyncSender extends AsyncTask<Void, Void, Void> {
private Context m_context;
private HashMap<String,ArrayList<byte[]>> m_msgHash;
public AsyncSender( Context context,
HashMap<String,ArrayList<byte[]>> msgHash )
{
m_context = context;
m_msgHash = msgHash;
}
@Override
protected Void doInBackground( Void... ignored )
{
// format: total msg lenth: 2
// number-of-relayIDs: 2
// for-each-relayid: relayid + '\n': varies
// message count: 1
// for-each-message: length: 2
// message: varies
// Build up a buffer containing everything but the total
// message length and number of relayIDs in the message.
try {
// Build up a buffer containing everything but the total
// message length and number of relayIDs in the message.
ByteArrayOutputStream store =
new ByteArrayOutputStream( MAX_BUF ); // mem
DataOutputStream outBuf = new DataOutputStream( store );
int msgLen = 4; // relayID count + protocol stuff
int nRelayIDs = 0;
Iterator<String> iter = msgHash.keySet().iterator();
Iterator<String> iter = m_msgHash.keySet().iterator();
while ( iter.hasNext() ) {
String relayID = iter.next();
int thisLen = 1 + relayID.length(); // string and '\n'
thisLen += 2; // message count
ArrayList<byte[]> msgs = msgHash.get( relayID );
ArrayList<byte[]> msgs = m_msgHash.get( relayID );
for ( byte[] msg : msgs ) {
thisLen += 2 + msg.length;
}
@ -649,10 +706,9 @@ public class RelayService extends XWService {
}
msgLen += thisLen;
}
// Now open a real socket, write size and proto, and
// copy in the formatted buffer
Socket socket = NetUtils.makeProxySocket( context, 8000 );
Socket socket = NetUtils.makeProxySocket( m_context, 8000 );
if ( null != socket ) {
DataOutputStream outStream =
new DataOutputStream( socket.getOutputStream() );
@ -667,6 +723,15 @@ public class RelayService extends XWService {
} catch ( java.io.IOException ioe ) {
DbgUtils.loge( ioe );
}
return null;
} // doInBackground
}
private static void sendToRelay( Context context,
HashMap<String,ArrayList<byte[]>> msgHash )
{
if ( null != msgHash ) {
new AsyncSender( context, msgHash ).execute();
} else {
DbgUtils.logf( "sendToRelay: null msgs" );
}

View file

@ -110,7 +110,7 @@ dict_getNextTileString( const DictionaryCtxt* dict, Tile tile,
} else {
cur += XP_STRLEN( cur ) + 1;
XP_Bool isSpecial = dict_faceIsBitmap( dict, tile );
if ( isSpecial ) {
if ( isSpecial || tile == dict->blankTile ) {
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
if ( cur < dict->charEnds[(XP_U16)*facep] ) {
result = cur;

View file

@ -2856,17 +2856,27 @@ server_formatDictCounts( ServerCtxt* server, XWStreamCtxt* stream,
nChars = dict_numTileFaces( dict );
for ( tile = 0, nPrinted = 0; ; ) {
XP_UCHAR buf[24];
XP_UCHAR buf[128];
XP_U16 count, value;
count = dict_numTiles( dict, tile );
if ( count > 0 ) {
const XP_UCHAR* face = dict_getTileString( dict, tile );
const XP_UCHAR* face = NULL;
XP_UCHAR faces[48] = {0};
XP_U16 len = 0;
for ( ; ; ) {
face = dict_getNextTileString( dict, tile, face );
if ( !face ) {
break;
}
const XP_UCHAR* fmt = len == 0? "%s" : ",%s";
len += XP_SNPRINTF( faces + len, sizeof(faces) - len, fmt, face );
}
value = dict_getTileValue( dict, tile );
XP_SNPRINTF( buf, sizeof(buf), (XP_UCHAR*)"%s: %d/%d",
face, count, value );
faces, count, value );
stream_catString( stream, buf );
}

View file

@ -19,6 +19,7 @@
XWLANG = PTBR4422
LANGCODE = pt_PT
ENC = UTF-8
DICTNOTE = "Lista com as 4422 palavras mais frequentes nas legendas em português brasileiro do site www.opensubtitles.org."
TARGET_TYPE ?= WINCE

View file

@ -32,6 +32,7 @@ g_con = None
g_sent = None
g_debug = False
g_skipSend = False # for debugging
g_columns = [ 'id', 'devid', 'connname', 'hid', 'msg64' ]
DEVTYPE_GCM = 3 # 3 == GCM
LINE_LEN = 76
@ -55,19 +56,51 @@ def init():
def getPendingMsgs( con, typ ):
cur = con.cursor()
query = """SELECT id, devid FROM msgs
query = """SELECT %s FROM msgs
WHERE devid IN (SELECT id FROM devices WHERE devtype=%d and NOT unreg)
AND NOT connname IN (SELECT connname FROM games WHERE dead); """
cur.execute(query % typ)
result = cur.fetchall()
cur.execute(query % (",".join( g_columns ), typ))
result = []
for row in cur:
rowObj = {}
for ii in range( len( g_columns ) ):
rowObj[g_columns[ii]] = row[ii]
result.append( rowObj )
if g_debug: print "getPendingMsgs=>", result
return result
def addClntVers( con, rows ):
query = """select clntVers[%s] from games where connname = '%s';"""
cur = con.cursor()
for row in rows:
cur.execute( query % (row['hid'], row['connname']))
if cur.rowcount == 1: row['clntVers'] = cur.fetchone()[0]
else: print "bad row count: ", cur.rowcount
con.commit()
return rows
def deleteMsgs( con, msgIDs ):
if 0 < len( msgIDs ):
query = "DELETE from msgs where id in (%s);" % ",".join(msgIDs)
try:
cur = con.cursor()
cur.execute(query)
con.commit()
except psycopg2.DatabaseError, e:
print 'Error %s' % e
except Exception as inst:
print "failed to execute", query
print type(inst)
print inst.args
print inst
def unregister( gcmid ):
global g_con
print "unregister(", gcmid, ")"
query = "UPDATE devices SET unreg=TRUE WHERE devid = '%s' and devtype = 3" % gcmid
g_con.cursor().execute( query )
g_con.commit()
def asGCMIds(con, devids, typ):
cur = con.cursor()
@ -76,13 +109,22 @@ def asGCMIds(con, devids, typ):
cur.execute( query )
return [elem[0] for elem in cur.fetchall()]
def notifyGCM( devids, typ ):
def notifyGCM( devids, typ, target ):
success = False
if typ == DEVTYPE_GCM:
if 3 <= target['clntVers']:
connname = "%s/%d" % (target['connname'], target['hid'])
data = { 'msgs64': [ target['msg64'] ],
'connname': connname,
}
else:
data = { 'getMoves': True, }
values = {
'data' : { 'getMoves': True, },
'data' : data,
'registration_ids': devids,
}
params = json.dumps( values )
req = urllib2.Request("https://android.googleapis.com/gcm/send", params )
req.add_header( 'Content-Type' , 'application/x-www-form-urlencoded;charset=UTF-8' )
req.add_header( 'Authorization' , 'key=' + mykey.myKey )
@ -92,11 +134,13 @@ def notifyGCM( devids, typ ):
if 'success' in asJson and 'failure' in asJson and len(devids) == asJson['success'] and 0 == asJson['failure']:
print "OK"
success = True
else:
print "Errors: "
print response
else:
print "not sending to", len(devids), "devices because typ ==", typ
return success
def shouldSend(val):
return val == 1
@ -113,14 +157,14 @@ def targetsAfterBackoff( msgs ):
global g_sent
targets = {}
for row in msgs:
msgid = row[0]
devid = row[1]
msgid = row['id']
devid = row['devid']
if not msgid in g_sent:
g_sent[msgid] = 0
g_sent[msgid] += 1
if shouldSend( g_sent[msgid] ):
targets[devid] = True
return targets.keys()
targets[devid] = row
return targets
# devids is an array of (msgid, devid) tuples
def pruneSent( devids ):
@ -129,7 +173,7 @@ def pruneSent( devids ):
lenBefore = len(g_sent)
msgids = []
for row in devids:
msgids.append(row[0])
msgids.append(row['id'])
for msgid in g_sent.keys():
if not msgid in msgids:
del g_sent[msgid]
@ -181,14 +225,21 @@ def main():
if g_debug: print
devids = getPendingMsgs( g_con, typ )
if 0 < len(devids):
devids = addClntVers( g_con, devids )
targets = targetsAfterBackoff( devids )
if 0 < len(targets):
if 0 < emptyCount: print ""
emptyCount = 0
print strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
print "devices needing notification:", targets, '=>',
notifyGCM( asGCMIds( g_con, targets, typ ), typ )
toDelete = []
for devid in targets.keys():
target = targets[devid]
if notifyGCM( asGCMIds(g_con, [devid], typ), typ, target )\
and 3 <= target['clntVers']:
toDelete.append( str(target['id']) )
pruneSent( devids )
deleteMsgs( g_con, toDelete )
elif g_debug: print "no targets after backoff"
else:
emptyCount += 1