take
@@ -25,10 +25,14 @@
New with this release
- F-Droid has stiffened their prohibition against including
- proprietary Google components. This release complies by removing
- "Google Cloud Messaging" – which never worked on
- f-droid installs anyway.
+ Communicate with relay via http when the custom protocol
+ isn't working (e.g. when a wifi router or firewall blocks
+ it.)
+ Improved translations for French, Japanese and Norwegian
+ (by Weblate volunteers)
+ Add a few menubar icons (thanks to The Noun Project)
+ Fix taps on left end of items in the Games List not always
+ selecting/unselecting them
(The full changelog
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/CommsTransport.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/CommsTransport.java
index 59830c635..4c51b9af5 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/CommsTransport.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/CommsTransport.java
@@ -387,32 +387,6 @@ public class CommsTransport implements TransportProcs,
return nSent;
}
- public void relayStatus( CommsRelayState newState )
- {
- Log.i( TAG, "relayStatus called; state=%s", newState.toString() );
-
- switch( newState ) {
- case COMMS_RELAYSTATE_UNCONNECTED:
- case COMMS_RELAYSTATE_DENIED:
- case COMMS_RELAYSTATE_CONNECT_PENDING:
- ConnStatusHandler.updateStatus( m_context, null,
- CommsConnType.COMMS_CONN_RELAY,
- false );
- break;
- case COMMS_RELAYSTATE_CONNECTED:
- case COMMS_RELAYSTATE_RECONNECTED:
- ConnStatusHandler.updateStatusOut( m_context, null,
- CommsConnType.COMMS_CONN_RELAY,
- true );
- break;
- case COMMS_RELAYSTATE_ALLCONNECTED:
- ConnStatusHandler.updateStatusIn( m_context, null,
- CommsConnType.COMMS_CONN_RELAY,
- true );
- break;
- }
- }
-
public void relayConnd( String room, int devOrder, boolean allHere,
int nMissing )
{
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java
index 92c476792..7bb484826 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameUtils.java
@@ -482,14 +482,12 @@ public class GameUtils {
if ( force ) {
HashMap games =
DBUtils.getGamesWithSendsPending( context );
- if ( 0 < games.size() ) {
- new ResendTask( context, games, filter, proc ).execute();
+ new ResendTask( context, games, filter, proc ).execute();
- System.arraycopy( sendTimes, 0, /* src */
- sendTimes, 1, /* dest */
- sendTimes.length - 1 );
- sendTimes[0] = now;
- }
+ System.arraycopy( sendTimes, 0, /* src */
+ sendTimes, 1, /* dest */
+ sendTimes.length - 1 );
+ sendTimes[0] = now;
}
}
@@ -1259,7 +1257,7 @@ public class GameUtils {
private HashMap m_games;
private ResendDoneProc m_doneProc;
private CommsConnType m_filter;
- private MultiMsgSink m_sink;
+ private int m_nSent = 0;
public ResendTask( Context context, HashMap games,
CommsConnType filter, ResendDoneProc proc )
@@ -1288,14 +1286,15 @@ public class GameUtils {
GameLock lock = new GameLock( rowid, false );
if ( lock.tryLock() ) {
CurGameInfo gi = new CurGameInfo( m_context );
- m_sink = new MultiMsgSink( m_context, rowid );
- GamePtr gamePtr = loadMakeGame( m_context, gi, m_sink, lock );
+ MultiMsgSink sink = new MultiMsgSink( m_context, rowid );
+ GamePtr gamePtr = loadMakeGame( m_context, gi, sink, lock );
if ( null != gamePtr ) {
int nSent = XwJNI.comms_resendAll( gamePtr, true,
m_filter, false );
gamePtr.release();
Log.d( TAG, "ResendTask.doInBackground(): sent %d "
+ "messages for rowid %d", nSent, rowid );
+ m_nSent += sink.numSent();
} else {
Log.d( TAG, "ResendTask.doInBackground(): loadMakeGame()"
+ " failed for rowid %d", rowid );
@@ -1320,8 +1319,7 @@ public class GameUtils {
protected void onPostExecute( Void unused )
{
if ( null != m_doneProc ) {
- int nSent = null == m_sink ? 0 : m_sink.numSent();
- m_doneProc.onResendDone( m_context, nSent );
+ m_doneProc.onResendDone( m_context, m_nSent );
}
}
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/MultiMsgSink.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/MultiMsgSink.java
index 01b6ed72c..0a08833e2 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/MultiMsgSink.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/MultiMsgSink.java
@@ -119,10 +119,6 @@ public class MultiMsgSink implements TransportProcs {
return nSent;
}
- public void relayStatus( CommsRelayState newState )
- {
- }
-
public void relayErrorProc( XWRELAY_ERROR relayErr )
{
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
index 984e57840..8f4a1870e 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
@@ -60,7 +60,9 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
public class RelayService extends XWService
implements NetStateCache.StateChangedIf {
@@ -69,6 +71,7 @@ public class RelayService extends XWService
private static final int MAX_BUF = MAX_SEND - 2;
private static final int REG_WAIT_INTERVAL = 10;
private static final int INITIAL_BACKOFF = 5;
+ private static final int UDP_FAIL_LIMIT = 5;
// One day, in seconds. Probably should be configurable.
private static final long MAX_KEEPALIVE_SECS = 24 * 60 * 60;
@@ -99,8 +102,8 @@ public class RelayService extends XWService
private static final String ROWID = "ROWID";
private static final String BINBUFFER = "BINBUFFER";
- private static Map s_packetsSentUDP = new HashMap<>();
- private static Map s_packetsSentWeb = new HashMap<>();
+ private static List s_packetsSentUDP = new ArrayList<>();
+ private static List s_packetsSentWeb = new ArrayList<>();
private static AtomicInteger s_nextPacketID = new AtomicInteger();
private static boolean s_gcmWorking = false;
private static boolean s_registered = false;
@@ -110,18 +113,14 @@ public class RelayService extends XWService
private static long s_curNextTimer;
static { resetBackoffTimer(); }
- private Thread m_fetchThread = null;
- private Thread m_UDPReadThread = null;
- private Thread m_UDPWriteThread = null;
- private DatagramSocket m_UDPSocket;
- private LinkedBlockingQueue m_queue =
- new LinkedBlockingQueue();
+ private Thread m_fetchThread = null; // no longer used
+ private AtomicReference m_UDPThreadsRef = new AtomicReference<>();
private Handler m_handler;
private Runnable m_onInactivity;
private int m_maxIntervalSeconds = 0;
private long m_lastGamePacketReceived;
- // m_nativeNotWorking: set to true if too many acks missed?
- private boolean m_nativeNotWorking = false;
+ private int m_nativeFailScore;
+ private boolean m_skipUPDSet;
private static DevIDType s_curType = DevIDType.ID_TYPE_NONE;
private static long s_regStartTime = 0;
@@ -318,7 +317,7 @@ public class RelayService extends XWService
// Exists to get incoming data onto the main thread
private static void postData( Context context, long rowid, byte[] msg )
{
- Log.d( TAG, "postData(): packet of length %d for token %d",
+ Log.d( TAG, "postData(): packet of length %d for token (rowid) %d",
msg.length, rowid );
if ( DBUtils.haveGame( context, rowid ) ) {
Intent intent = getIntentTo( context, MsgCmds.RECEIVE )
@@ -386,6 +385,8 @@ public class RelayService extends XWService
}
}
};
+
+ m_skipUPDSet = XWPrefs.getSkipToWebAPI( this );
}
@Override
@@ -400,7 +401,7 @@ public class RelayService extends XWService
cmd = null;
}
if ( null != cmd ) {
- Log.d( TAG, "onStartCommand(): cmd=%s", cmd.toString() );
+ // Log.d( TAG, "onStartCommand(): cmd=%s", cmd.toString() );
switch( cmd ) {
case PROCESS_GAME_MSGS:
String[] relayIDs = new String[1];
@@ -554,278 +555,64 @@ public class RelayService extends XWService
private void startUDPThreadsIfNot()
{
if ( XWApp.UDP_ENABLED && relayEnabled( this ) ) {
- if ( null == m_UDPReadThread ) {
- m_UDPReadThread = new Thread( null, new Runnable() {
- public void run() {
-
- connectSocket(); // block until this is done
- startWriteThread();
-
- Log.i( TAG, "read thread running" );
- byte[] buf = new byte[1024];
- for ( ; ; ) {
- DatagramPacket packet =
- new DatagramPacket( buf, buf.length );
- try {
- m_UDPSocket.receive( packet );
- resetExitTimer();
- gotPacket( packet );
- } catch ( java.io.InterruptedIOException iioe ) {
- // DbgUtils.logf( "FYI: udp receive timeout" );
- } catch( java.io.IOException ioe ) {
- break;
- }
- }
- Log.i( TAG, "read thread exiting" );
- }
- }, getClass().getName() );
- m_UDPReadThread.start();
- } else {
- Log.i( TAG, "m_UDPReadThread not null and assumed to be running" );
+ synchronized ( m_UDPThreadsRef ) {
+ if ( null == m_UDPThreadsRef.get() ) {
+ UDPThreads threads = new UDPThreads();
+ m_UDPThreadsRef.set( threads );
+ threads.start();
+ }
}
} else {
Log.i( TAG, "startUDPThreadsIfNot(): UDP disabled" );
}
} // startUDPThreadsIfNot
- private void connectSocket()
- {
- if ( null == m_UDPSocket ) {
- int port = XWPrefs.getDefaultRelayPort( this );
- String host = XWPrefs.getDefaultRelayHost( this );
- try {
- m_UDPSocket = new DatagramSocket();
- m_UDPSocket.setSoTimeout(30 * 1000); // timeout so we can log
-
- InetAddress addr = InetAddress.getByName( host );
- m_UDPSocket.connect( addr, port ); // remember this address
- Log.d( TAG, "connectSocket(%s:%d): m_UDPSocket now %H",
- host, port, m_UDPSocket );
- } catch( java.net.SocketException se ) {
- Log.ex( TAG, se );
- Assert.fail();
- } catch( java.net.UnknownHostException uhe ) {
- Log.ex( TAG, uhe );
- }
- } else {
- Assert.assertTrue( m_UDPSocket.isConnected() );
- Log.i( TAG, "m_UDPSocket not null" );
- }
- }
-
private boolean skipNativeSend()
{
- boolean skip = m_nativeNotWorking;
- if ( ! skip ) {
- skip = XWPrefs.getSkipToWebAPI( RelayService.this );
- }
+ boolean skip = m_nativeFailScore > UDP_FAIL_LIMIT || m_skipUPDSet;
+ // Log.d( TAG, "skipNativeSend(score=%d)) => %b", m_nativeFailScore, skip );
return skip;
}
- private void startWriteThread()
+ // So it's a map. The timer iterates over the whole map, which should
+ // never be *that* big, and pulls everything older than 10 seconds. If
+ // anything in that list isn't an ACK (since ACKs will always be there
+ // because they're not ACK'd) then the whole thing gets resent.
+
+ private void noteSent( PacketData packet, boolean fromUDP )
{
- if ( null == m_UDPWriteThread ) {
- m_UDPWriteThread = new Thread( null, new Runnable() {
- public void run() {
- Log.i( TAG, "write thread starting" );
- for ( ; ; ) {
- boolean exitNow = false;
- boolean useWeb = skipNativeSend();
- List dataListUDP = new ArrayList<>();
- List dataListWeb = new ArrayList<>();
- try {
- for ( PacketData outData = m_queue.take(); // blocks
- null != outData;
- outData = m_queue.poll() ) { // doesn't block
- if ( outData.isEOQ() ) {
- exitNow = true;
- break;
- }
- if ( useWeb || outData.getForWeb() ) {
- dataListWeb.add(outData);
- } else {
- dataListUDP.add(outData);
- }
- }
- } catch ( InterruptedException ie ) {
- Log.w( TAG, "write thread killed" );
- break;
- }
- if ( exitNow ) {
- Log.i( TAG, "stopping write thread" );
- break;
- }
-
- sendViaWeb( dataListWeb );
- sendViaUDP( dataListUDP );
-
- resetExitTimer();
- ConnStatusHandler.showSuccessOut();
- }
- Log.i( TAG, "write thread exiting" );
- }
- }, getClass().getName() );
- m_UDPWriteThread.start();
- } else {
- Log.i( TAG, "m_UDPWriteThread not null and assumed to "
- + "be running" );
- }
- }
-
- private int sendViaWeb( List packets )
- {
- Log.d( TAG, "sendViaWeb(): sending %d at once", packets.size() );
- int sentLen = 0;
- if ( packets.size() > 0 ) {
- HttpURLConnection conn = NetUtils.makeHttpRelayConn( this, "post" );
- if ( null == conn ) {
- Log.e( TAG, "sendViaWeb(): null conn for POST" );
- } else {
- try {
- JSONArray dataArray = new JSONArray();
- for ( PacketData packet : packets ) {
- Assert.assertFalse( packet.isEOQ() );
- byte[] datum = packet.assemble();
- dataArray.put( Utils.base64Encode(datum) );
- sentLen += datum.length;
- }
- JSONObject params = new JSONObject();
- params.put( "data", dataArray );
-
- String result = NetUtils.runConn(conn, params);
- if ( null != result ) {
- Log.d( TAG, "sendViaWeb(): POST(%s) => %s", params, result );
- JSONObject resultObj = new JSONObject( result );
- JSONArray resData = resultObj.getJSONArray( "data" );
- int nReplies = resData.length();
- Log.d( TAG, "sendViaWeb(): got %d replies", nReplies );
-
- noteSent( packets, s_packetsSentWeb ); // before we process the acks below :-)
-
- for ( int ii = 0; ii < nReplies; ++ii ) {
- byte[] datum = Utils.base64Decode( resData.getString( ii ) );
- // PENDING: skip ack or not
- gotPacket( datum, false, false );
- }
- } else {
- Log.e( TAG, "sendViaWeb(): failed result for POST" );
- }
- } catch ( JSONException ex ) {
- Assert.assertFalse( BuildConfig.DEBUG );
- }
+ Log.d( TAG, "Sent (fromUDP=%b) packet: cmd=%s, id=%d",
+ fromUDP, packet.m_cmd.toString(), packet.m_packetID );
+ if ( fromUDP || packet.m_cmd != XWRelayReg.XWPDEV_ACK ) {
+ List list = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
+ synchronized( list ) {
+ list.add(packet );
}
}
- return sentLen;
}
- private int sendViaUDP( List packets )
+ private void noteSent( List packets, boolean fromUDP )
{
- int sentLen = 0;
+ long nowMS = System.currentTimeMillis();
+ List map = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
+ Log.d( TAG, "noteSent(fromUDP=%b): adding %d; size before: %d",
+ fromUDP, packets.size(), map.size() );
for ( PacketData packet : packets ) {
- boolean getOut = true;
- byte[] data = packet.assemble();
- try {
- DatagramPacket udpPacket = new DatagramPacket( data, data.length );
- m_UDPSocket.send( udpPacket );
-
- sentLen += udpPacket.getLength();
- noteSent( packet, s_packetsSentUDP );
- getOut = false;
- } catch ( java.net.SocketException se ) {
- Log.ex( TAG, se );
- Log.i( TAG, "Restarting threads to force"
- + " new socket" );
- m_handler.post( new Runnable() {
- public void run() {
- stopUDPThreadsIf();
- }
- } );
- } catch ( java.io.IOException ioe ) {
- Log.ex( TAG, ioe );
- } catch ( NullPointerException npe ) {
- Log.w( TAG, "network problem; dropping packet" );
- }
- if ( getOut ) {
- break;
+ if ( fromUDP ) {
+ packet.setSentMS( nowMS );
}
+ noteSent( packet, fromUDP );
}
-
- if ( sentLen > 0 ) {
- startAckTimer( packets );
- }
-
- return sentLen;
- }
-
- private void startAckTimer( final List packets )
- {
- Runnable ackTimer = new Runnable() {
- @Override
- public void run() {
- List forResend = new ArrayList<>();
- Log.d( TAG, "ackTimer.run() called" );
- synchronized ( s_packetsSentUDP ) {
- for ( PacketData packet : packets ) {
- PacketData stillThere = s_packetsSentUDP.remove(packet.m_packetID);
- if ( stillThere != null ) {
- Log.d( TAG, "packed %d not yet acked; resending",
- stillThere.m_packetID );
- stillThere.setForWeb();
- forResend.add( stillThere );
- }
- }
- }
- m_queue.addAll( forResend );
- }
- };
- m_handler.postDelayed( ackTimer, 10 * 1000 );
- }
-
- private void noteSent( PacketData packet, Map map )
- {
- int pid = packet.m_packetID;
- Log.d( TAG, "Sent [udp?] packet: cmd=%s, id=%d",
- packet.m_cmd.toString(), pid );
- if ( packet.m_cmd != XWRelayReg.XWPDEV_ACK ) {
- synchronized( map ) {
- map.put( pid, packet );
- }
- }
- }
-
- private void noteSent( List packets, Map map )
- {
- for ( PacketData packet : packets ) {
- noteSent( packet, map );
- }
+ Log.d( TAG, "noteSent(fromUDP=%b): size after: %d", fromUDP, map.size() );
}
private void stopUDPThreadsIf()
{
DbgUtils.assertOnUIThread();
- if ( null != m_UDPWriteThread ) {
- // can't add null
- m_queue.add( new PacketData() );
- try {
- Log.d( TAG, "joining m_UDPWriteThread" );
- m_UDPWriteThread.join();
- Log.d( TAG, "SUCCESSFULLY joined m_UDPWriteThread" );
- } catch( java.lang.InterruptedException ie ) {
- Log.ex( TAG, ie );
- }
- m_UDPWriteThread = null;
- m_queue.clear();
- }
- if ( null != m_UDPSocket && null != m_UDPReadThread ) {
- m_UDPSocket.close();
- try {
- m_UDPReadThread.join();
- } catch( java.lang.InterruptedException ie ) {
- Log.ex( TAG, ie );
- }
- m_UDPReadThread = null;
- m_UDPSocket = null;
+ UDPThreads threads = m_UDPThreadsRef.getAndSet( null );
+ if ( null != threads ) {
+ threads.stop();
}
}
@@ -928,7 +715,7 @@ public class RelayService extends XWService
if ( resetBackoff ) {
resetBackoffTimer();
}
- }
+ } // gotPacket()
private void gotPacket( DatagramPacket packet )
{
@@ -1014,14 +801,14 @@ public class RelayService extends XWService
private void requestMessagesImpl( XWRelayReg reg )
{
- ByteArrayOutputStream bas = new ByteArrayOutputStream();
try {
DevIDType[] typp = new DevIDType[1];
String devid = getDevID( typp );
if ( null != devid ) {
+ ByteArrayOutputStream bas = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream( bas );
writeVLIString( out, devid );
- Log.d(TAG, "requestMessagesImpl(): devid: %s; type: " + typp[0], devid );
+ // Log.d( TAG, "requestMessagesImpl(): devid: %s; type: " + typp[0], devid );
postPacket( bas, reg );
} else {
Log.d(TAG, "requestMessagesImpl(): devid is null" );
@@ -1149,7 +936,11 @@ public class RelayService extends XWService
private void postPacket( ByteArrayOutputStream bas, XWRelayReg cmd )
{
- m_queue.add( new PacketData( bas, cmd ) );
+ startUDPThreadsIfNot();
+ UDPThreads threads = m_UDPThreadsRef.get();
+ if ( threads != null ) {
+ threads.add( new PacketData( bas, cmd ) );
+ }
// 0 ok; thread will often have sent already!
// DbgUtils.logf( "postPacket() done; %d in queue", m_queue.size() );
}
@@ -1213,6 +1004,277 @@ public class RelayService extends XWService
}
}
+ private class UDPThreads {
+ private DatagramSocket m_UDPSocket;
+ private LinkedBlockingQueue m_queue =
+ new LinkedBlockingQueue();
+ private Thread m_UDPReadThread;
+ private Thread m_UDPWriteThread;
+
+ UDPThreads() {}
+
+ void start()
+ {
+ m_UDPReadThread = new Thread( null, new Runnable() {
+ public void run() {
+
+ connectSocket(); // block until this is done
+ startWriteThread();
+
+ Log.i( TAG, "read thread running" );
+ byte[] buf = new byte[1024];
+ for ( ; ; ) {
+ DatagramPacket packet =
+ new DatagramPacket( buf, buf.length );
+ try {
+ m_UDPSocket.receive( packet );
+ resetExitTimer();
+ gotPacket( packet );
+ } catch ( java.io.InterruptedIOException iioe ) {
+ // DbgUtils.logf( "FYI: udp receive timeout" );
+ } catch( java.io.IOException ioe ) {
+ break;
+ }
+ }
+ Log.i( TAG, "read thread exiting" );
+ }
+ }, getClass().getName() );
+ m_UDPReadThread.start();
+ }
+
+ void stop()
+ {
+ m_queue.add( new EOQPacketData() ); // will kill the writer thread
+ }
+
+ void add( PacketData packet )
+ {
+ m_queue.add( packet );
+ }
+
+ private void connectSocket()
+ {
+ if ( null == m_UDPSocket ) {
+ int port = XWPrefs.getDefaultRelayPort( RelayService.this );
+ String host = XWPrefs.getDefaultRelayHost( RelayService.this );
+ try {
+ m_UDPSocket = new DatagramSocket();
+ m_UDPSocket.setSoTimeout(30 * 1000); // timeout so we can log
+
+ InetAddress addr = InetAddress.getByName( host );
+ m_UDPSocket.connect( addr, port ); // remember this address
+ Log.d( TAG, "connectSocket(%s:%d): m_UDPSocket now %H",
+ host, port, m_UDPSocket );
+ } catch( java.net.SocketException se ) {
+ Log.ex( TAG, se );
+ Assert.fail();
+ } catch( java.net.UnknownHostException uhe ) {
+ Log.ex( TAG, uhe );
+ }
+ } else {
+ Assert.assertTrue( m_UDPSocket.isConnected() );
+ Log.i( TAG, "m_UDPSocket not null" );
+ }
+ }
+
+ private void startWriteThread()
+ {
+ Assert.assertNull( m_UDPWriteThread );
+
+ m_UDPWriteThread = new Thread( null, new Runnable() {
+ public void run() {
+ Log.i( TAG, "write thread starting" );
+ for ( boolean gotEOQ = false; !gotEOQ; ) {
+ List dataListUDP = new ArrayList<>();
+ List dataListWeb = new ArrayList<>();
+ PacketData outData;
+ try {
+ long ts = s_packetsSentUDP.size() > 0 ? 10 : 3600;
+ Log.d( TAG, "blocking %d sec on poll()", ts );
+ for ( outData = m_queue.poll(ts, TimeUnit.SECONDS);
+ null != outData;
+ outData = m_queue.poll() ) { // doesn't block
+ if ( outData instanceof EOQPacketData ) {
+ gotEOQ = true;
+ break;
+ } else if ( skipNativeSend() || outData.getForWeb() ) {
+ dataListWeb.add (outData );
+ } else {
+ dataListUDP.add( outData );
+ }
+ }
+ } catch ( InterruptedException ie ) {
+ Log.w( TAG, "write thread killed" );
+ break;
+ }
+
+ sendViaWeb( dataListWeb );
+ sendViaUDP( dataListUDP );
+
+ resetExitTimer();
+ runUDPAckTimer();
+
+ ConnStatusHandler.showSuccessOut();
+ }
+
+ Log.i( TAG, "write thread killing read thread" );
+
+ // now kill the read thread
+ m_UDPSocket.close();
+ try {
+ m_UDPReadThread.join();
+ } catch( java.lang.InterruptedException ie ) {
+ Log.ex( TAG, ie );
+ }
+
+ Log.i( TAG, "write thread exiting" );
+ }
+ }, getClass().getName() );
+ m_UDPWriteThread.start();
+ }
+
+ private int sendViaWeb( List packets )
+ {
+ Log.d( TAG, "sendViaWeb(): sending %d at once", packets.size() );
+ int sentLen = 0;
+ if ( packets.size() > 0 ) {
+ HttpURLConnection conn = NetUtils.makeHttpRelayConn( RelayService.this, "post" );
+ if ( null == conn ) {
+ Log.e( TAG, "sendViaWeb(): null conn for POST" );
+ } else {
+ try {
+ JSONArray dataArray = new JSONArray();
+ for ( PacketData packet : packets ) {
+ Assert.assertFalse( packet instanceof EOQPacketData );
+ byte[] datum = packet.assemble();
+ dataArray.put( Utils.base64Encode(datum) );
+ sentLen += datum.length;
+ }
+ JSONObject params = new JSONObject();
+ params.put( "data", dataArray );
+
+ String result = NetUtils.runConn( conn, params );
+ boolean succeeded = null != result;
+ if ( succeeded ) {
+ Log.d( TAG, "sendViaWeb(): POST(%s) => %s", params, result );
+ JSONObject resultObj = new JSONObject( result );
+ JSONArray resData = resultObj.getJSONArray( "data" );
+ int nReplies = resData.length();
+ // Log.d( TAG, "sendViaWeb(): got %d replies", nReplies );
+
+ noteSent( packets, false ); // before we process the acks below :-)
+
+ for ( int ii = 0; ii < nReplies; ++ii ) {
+ byte[] datum = Utils.base64Decode( resData.getString( ii ) );
+ // PENDING: skip ack or not
+ gotPacket( datum, false, false );
+ }
+ } else {
+ Log.e( TAG, "sendViaWeb(): failed result for POST" );
+
+ }
+
+ ConnStatusHandler.updateStatus( RelayService.this, null,
+ CommsConnType.COMMS_CONN_RELAY,
+ succeeded );
+ } catch ( JSONException ex ) {
+ Assert.assertFalse( BuildConfig.DEBUG );
+ }
+ }
+ }
+ return sentLen;
+ }
+
+ private int sendViaUDP( List packets )
+ {
+ int sentLen = 0;
+
+ if ( packets.size() > 0 ) {
+ noteSent( packets, true );
+ for ( PacketData packet : packets ) {
+ boolean getOut = true;
+ byte[] data = packet.assemble();
+ try {
+ DatagramPacket udpPacket = new DatagramPacket( data, data.length );
+ m_UDPSocket.send( udpPacket );
+
+ sentLen += udpPacket.getLength();
+ // packet.setSentMS( nowMS );
+ getOut = false;
+ } catch ( java.net.SocketException se ) {
+ Log.ex( TAG, se );
+ Log.i( TAG, "Restarting threads to force new socket" );
+ ConnStatusHandler.updateStatusOut( RelayService.this, null,
+ CommsConnType.COMMS_CONN_RELAY,
+ true );
+
+ m_handler.post( new Runnable() {
+ public void run() {
+ stopUDPThreadsIf();
+ }
+ } );
+ break;
+ } catch ( java.io.IOException ioe ) {
+ Log.ex( TAG, ioe );
+ } catch ( NullPointerException npe ) {
+ Log.w( TAG, "network problem; dropping packet" );
+ }
+ if ( getOut ) {
+ break;
+ }
+ }
+
+ ConnStatusHandler.updateStatus( RelayService.this, null,
+ CommsConnType.COMMS_CONN_RELAY,
+ sentLen > 0 );
+ }
+
+ return sentLen;
+ }
+
+ private long m_lastRunMS = 0;
+ private void runUDPAckTimer()
+ {
+ long nowMS = System.currentTimeMillis();
+ if ( m_lastRunMS + 3000 > nowMS ) { // never more frequently than 3 sec.
+ // Log.d( TAG, "runUDPAckTimer(): too soon, so skipping" );
+ } else {
+ m_lastRunMS = nowMS;
+
+ long minSentMS = nowMS - 10000; // 10 seconds ago
+ long prevSentMS = 0;
+ List forResend = new ArrayList<>();
+ boolean foundNonAck = false;
+ synchronized ( s_packetsSentUDP ) {
+ Iterator iter;
+ for ( iter = s_packetsSentUDP.iterator(); iter.hasNext(); ) {
+ PacketData packet = iter.next();
+ long sentMS = packet.getSentMS();
+ Assert.assertTrue( prevSentMS <= sentMS );
+ prevSentMS = sentMS;
+ if ( sentMS > minSentMS ) {
+ break;
+ }
+
+ forResend.add( packet );
+ if ( packet.m_cmd != XWRelayReg.XWPDEV_ACK ) {
+ foundNonAck = true;
+ ++m_nativeFailScore;
+ }
+ iter.remove();
+ }
+ Log.d( TAG, "runUDPAckTimer(): %d too-new packets remaining",
+ s_packetsSentUDP.size() );
+ }
+ if ( foundNonAck ) {
+ Log.d( TAG, "runUDPAckTimer(): reposting %d packets", forResend.size() );
+ m_queue.addAll( forResend );
+ }
+ }
+ }
+
+ }
+
private static class AsyncSender extends AsyncTask {
private Context m_context;
private HashMap> m_msgHash;
@@ -1227,7 +1289,6 @@ public class RelayService extends XWService
@Override
protected Void doInBackground( Void... ignored )
{
- Assert.assertFalse( XWPrefs.getSkipToWebAPI( m_context ) );
// format: total msg lenth: 2
// number-of-relayIDs: 2
// for-each-relayid: relayid + '\n': varies
@@ -1276,7 +1337,6 @@ public class RelayService extends XWService
// Now open a real socket, write size and proto, and
// copy in the formatted buffer
- Assert.assertFalse( XWPrefs.getSkipToWebAPI( m_context ) );
Socket socket = NetUtils.makeProxySocket( m_context, 8000 );
if ( null != socket ) {
DataOutputStream outStream =
@@ -1358,27 +1418,47 @@ public class RelayService extends XWService
return nextPacketID;
}
- private static void noteAck( int packetID, boolean fromUDP )
+ private void noteAck( int packetID, boolean fromUDP )
{
- PacketData packet;
- Map map = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
+ Assert.assertTrue( packetID != 0 );
+ List map = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
synchronized( map ) {
- packet = map.remove( packetID );
+ PacketData packet = null;
+ Iterator iter = map.iterator();
+ for ( iter = map.iterator(); iter.hasNext(); ) {
+ PacketData next = iter.next();
+ if ( next.m_packetID == packetID ) {
+ packet = next;
+ iter.remove();
+ break;
+ }
+ }
+
if ( packet != null ) {
- Log.d( TAG, "noteAck(fromUDP=%b): removed for id %d: %s",
- fromUDP, packetID, packet );
+ // Log.d( TAG, "noteAck(fromUDP=%b): removed for id %d: %s",
+ // fromUDP, packetID, packet );
+ if ( fromUDP ) {
+ --m_nativeFailScore;
+ }
} else {
Log.w( TAG, "Weird: got ack %d but never sent", packetID );
}
if ( BuildConfig.DEBUG ) {
ArrayList pstrs = new ArrayList<>();
- for ( Integer pkid : map.keySet() ) {
- pstrs.add( map.get(pkid).toString() );
+ for ( PacketData datum : map ) {
+ pstrs.add( String.format("%d", datum.m_packetID ) );
}
Log.d( TAG, "noteAck(fromUDP=%b): Got ack for %d; there are %d unacked packets: %s",
fromUDP, packetID, map.size(), TextUtils.join( ",", pstrs ) );
}
}
+
+ // If we get an ACK, things are working, even if it's not found above
+ // (which would be the case for an ACK sent via web, which we don't
+ // save.)
+ ConnStatusHandler.updateStatus( this, null,
+ CommsConnType.COMMS_CONN_RELAY,
+ true );
}
// Called from any thread
@@ -1490,7 +1570,7 @@ public class RelayService extends XWService
result = figureBackoffSeconds();
}
- Log.d( TAG, "getMaxIntervalSeconds() => %d", result );
+ Log.d( TAG, "getMaxIntervalSeconds() => %d", result ); // WFT? went from 40 to 1000
return result;
}
@@ -1526,7 +1606,8 @@ public class RelayService extends XWService
private boolean shouldMaintainConnection()
{
boolean result = relayEnabled( this )
- && (XWApp.GCM_IGNORED || !s_gcmWorking);
+ && (!s_gcmWorking || XWPrefs.getIgnoreGCM( this ));
+
if ( result ) {
long interval = Utils.getCurSeconds() - m_lastGamePacketReceived;
result = interval < MAX_KEEPALIVE_SECS;
@@ -1564,7 +1645,7 @@ public class RelayService extends XWService
Assert.assertTrue( diff < Integer.MAX_VALUE );
result = (int)diff;
}
- Log.d( TAG, "figureBackoffSeconds() => %d", result );
+ // Log.d( TAG, "figureBackoffSeconds() => %d", result );
return result;
}
@@ -1583,16 +1664,12 @@ public class RelayService extends XWService
public byte[] m_header;
public int m_packetID;
private long m_created;
- private boolean m_useWeb;
+ private long m_sentUDP;
- public PacketData() {
- m_bas = null;
- m_created = System.currentTimeMillis();
- }
+ private PacketData() {}
public PacketData( ByteArrayOutputStream bas, XWRelayReg cmd )
{
- this();
m_bas = bas;
m_cmd = cmd;
}
@@ -1604,10 +1681,9 @@ public class RelayService extends XWService
System.currentTimeMillis() - m_created );
}
- void setForWeb() { m_useWeb = true; }
- boolean getForWeb() { return m_useWeb; }
-
- public boolean isEOQ() { return 0 == getLength(); }
+ void setSentMS( long ms ) { m_sentUDP = ms; }
+ long getSentMS() { return m_sentUDP; }
+ boolean getForWeb() { return m_sentUDP != 0; }
public int getLength()
{
@@ -1647,4 +1723,7 @@ public class RelayService extends XWService
}
}
}
+
+ // Exits only to exist, so instanceof can distinguish
+ private class EOQPacketData extends PacketData {}
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
index de49a2c2f..4c073a829 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
@@ -443,7 +443,7 @@ public class Utils {
{
// Note: an int is big enough for *seconds* (not milliseconds) since 1970
// until 2038
- long millis = new Date().getTime();
+ long millis = System.currentTimeMillis();
int result = (int)(millis / 1000);
return result;
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
index dbf740b99..b76ed4bfb 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java
@@ -39,7 +39,6 @@ public class XWApp extends Application {
public static final boolean ATTACH_SUPPORTED = false;
public static final boolean LOG_LIFECYLE = false;
public static final boolean DEBUG_EXP_TIMERS = false;
- public static final boolean GCM_IGNORED = false;
public static final boolean UDP_ENABLED = true;
public static final boolean SMS_INVITE_ENABLED = true;
public static final boolean LOCUTILS_ENABLED = false;
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWPrefs.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWPrefs.java
index 6747f05a2..ccf1779b7 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWPrefs.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWPrefs.java
@@ -67,6 +67,16 @@ public class XWPrefs {
return getPrefsBoolean( context, R.string.key_enable_nfc_toself, false );
}
+ public static boolean getIgnoreGCM( Context context )
+ {
+ return getPrefsBoolean( context, R.string.key_ignore_gcm, false );
+ }
+
+ public static boolean getToastGCM( Context context )
+ {
+ return getPrefsBoolean( context, R.string.key_show_gcm, false );
+ }
+
public static boolean getRelayInviteToSelfEnabled( Context context )
{
return getPrefsBoolean( context, R.string.key_enable_relay_toself, false );
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java
index 49a83d74d..28bf81107 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/JNIThread.java
@@ -266,15 +266,19 @@ public class JNIThread extends Thread {
}
public boolean busy()
- { // synchronize this!!!
+ {
boolean result = false;
+
+ // Docs: The returned iterator is a "weakly consistent" iterator that
+ // will never throw ConcurrentModificationException, and guarantees to
+ // traverse elements as they existed upon construction of the
+ // iterator, and may (but is not guaranteed to) reflect any
+ // modifications subsequent to construction.
Iterator iter = m_queue.iterator();
- while ( iter.hasNext() ) {
- if ( iter.next().m_isUIEvent ) {
- result = true;
- break;
- }
+ while ( iter.hasNext() && !result ) {
+ result = iter.next().m_isUIEvent;
}
+
return result;
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/TransportProcs.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/TransportProcs.java
index 5d3f381a4..ce1a31ea8 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/TransportProcs.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/TransportProcs.java
@@ -38,7 +38,6 @@ public interface TransportProcs {
, COMMS_RELAYSTATE_RECONNECTED
, COMMS_RELAYSTATE_ALLCONNECTED
};
- void relayStatus( CommsRelayState newState );
void relayConnd( String room, int devOrder, boolean allHere, int nMissing );
diff --git a/xwords4/android/app/src/main/res/menu/board_menu.xml b/xwords4/android/app/src/main/res/menu/board_menu.xml
index a027b9aaf..4814192e3 100644
--- a/xwords4/android/app/src/main/res/menu/board_menu.xml
+++ b/xwords4/android/app/src/main/res/menu/board_menu.xml
@@ -32,7 +32,7 @@
- key_enable_nfc_toself
key_enable_sms_toself
key_enable_relay_toself
+ key_ignore_gcm
+ key_show_gcm
key_nag_intervals
key_download_path
key_got_langdict
diff --git a/xwords4/android/app/src/main/res/values/strings.xml b/xwords4/android/app/src/main/res/values/strings.xml
index 57aa7015b..f6616d84e 100644
--- a/xwords4/android/app/src/main/res/values/strings.xml
+++ b/xwords4/android/app/src/main/res/values/strings.xml
@@ -1692,7 +1692,7 @@
CrossWords for Android, Version %1$s,
rev %2$s, built on %3$s.
- Copyright (C) 1998-2017 by Eric
+ Copyright (C) 1998-2018 by Eric
House. This free/open source software is released under the GNU Public
License.
@@ -2601,6 +2601,12 @@
Enable relay invites to self
(To aid testing and debugging)
+ Ignore incoming GCM messages
+ Mimic life without a google account
+
+ Show SMS sends, receives
+ Show GCM receives
+
- One move sent
diff --git a/xwords4/android/app/src/main/res/xml/xwprefs.xml b/xwords4/android/app/src/main/res/xml/xwprefs.xml
index 43dc96048..67963ed6e 100644
--- a/xwords4/android/app/src/main/res/xml/xwprefs.xml
+++ b/xwords4/android/app/src/main/res/xml/xwprefs.xml
@@ -390,28 +390,6 @@
android:defaultValue="false"
/>
-
-
-
-
-
-
-
@@ -420,6 +398,15 @@
android:summary="@string/enable_relay_toself_summary"
android:defaultValue="false"
/>
+
+
+
+
+
+
+
+
+
diff --git a/xwords4/android/app/src/xw4/java/org/eehouse/android/xw4/GCMIntentService.java b/xwords4/android/app/src/xw4/java/org/eehouse/android/xw4/GCMIntentService.java
index 7175e56d9..5069dea57 100644
--- a/xwords4/android/app/src/xw4/java/org/eehouse/android/xw4/GCMIntentService.java
+++ b/xwords4/android/app/src/xw4/java/org/eehouse/android/xw4/GCMIntentService.java
@@ -34,6 +34,8 @@ import junit.framework.Assert;
public class GCMIntentService extends GCMBaseIntentService {
private static final String TAG = GCMIntentService.class.getSimpleName();
+ private Boolean m_toastGCM;
+
public GCMIntentService()
{
super( BuildConfig.GCM_SENDER_ID );
@@ -67,14 +69,19 @@ public class GCMIntentService extends GCMBaseIntentService {
protected void onMessage( Context context, Intent intent )
{
Log.d( TAG, "onMessage()" );
- notifyRelayService( context, true );
- String value;
- boolean ignoreIt = XWApp.GCM_IGNORED;
- if ( ignoreIt ) {
- Log.d( TAG, "received GCM but ignoring it" );
+ if ( null == m_toastGCM ) {
+ m_toastGCM = new Boolean( XWPrefs.getToastGCM( context ) );
+ }
+
+ if ( XWPrefs.getIgnoreGCM( context ) ) {
+ String logMsg = "received GCM but ignoring it";
+ Log.d( TAG, logMsg );
+ DbgUtils.showf( context, logMsg );
} else {
- value = intent.getStringExtra( "checkUpdates" );
+ notifyRelayService( context, true );
+
+ String value = intent.getStringExtra( "checkUpdates" );
if ( null != value && Boolean.parseBoolean( value ) ) {
UpdateCheckReceiver.checkVersions( context, true );
}
@@ -82,6 +89,9 @@ public class GCMIntentService extends GCMBaseIntentService {
value = intent.getStringExtra( "getMoves" );
if ( null != value && Boolean.parseBoolean( value ) ) {
RelayService.timerFired( context );
+ if ( m_toastGCM ) {
+ DbgUtils.showf( context, "onMessage(): got 'getMoves'" );
+ }
}
value = intent.getStringExtra( "msgs64" );
@@ -90,6 +100,11 @@ public class GCMIntentService extends GCMBaseIntentService {
try {
JSONArray msgs64 = new JSONArray( value );
String[] strs64 = new String[msgs64.length()];
+ if ( m_toastGCM ) {
+ DbgUtils.showf( context, "onMessage(): got %d msgs",
+ strs64.length );
+ }
+
for ( int ii = 0; ii < strs64.length; ++ii ) {
strs64[ii] = msgs64.optString(ii);
}
@@ -100,6 +115,7 @@ public class GCMIntentService extends GCMBaseIntentService {
}
} catch (org.json.JSONException jse ) {
Log.ex( TAG, jse );
+ Assert.assertFalse( BuildConfig.DEBUG );
}
}
@@ -145,10 +161,8 @@ public class GCMIntentService extends GCMBaseIntentService {
private void notifyRelayService( Context context, boolean working )
{
- if ( working && XWApp.GCM_IGNORED ) {
- working = false;
+ if ( !XWPrefs.getIgnoreGCM( context ) ) {
+ RelayService.gcmConfirmed( context, working );
}
- RelayService.gcmConfirmed( context, working );
}
-
}
diff --git a/xwords4/android/app/src/xw4d/res/values/.gitignore b/xwords4/android/app/src/xw4d/res/values/.gitignore
new file mode 100644
index 000000000..be5186f01
--- /dev/null
+++ b/xwords4/android/app/src/xw4d/res/values/.gitignore
@@ -0,0 +1 @@
+strings.xml
diff --git a/xwords4/android/img_src/archive.svg b/xwords4/android/img_src/archive.svg
index c12a564e8..737fc926e 100644
--- a/xwords4/android/img_src/archive.svg
+++ b/xwords4/android/img_src/archive.svg
@@ -8,15 +8,15 @@
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
data-name="Layer 1"
- viewBox="0 0 100 100"
+ viewBox="0 0 66 82"
x="0px"
y="0px"
id="svg3396"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="archive.svg"
- width="100"
- height="100">
+ width="66"
+ height="82">
@@ -41,40 +41,46 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
- inkscape:window-height="1016"
+ inkscape:window-height="1163"
id="namedview3412"
showgrid="true"
showborder="false"
inkscape:zoom="6.616"
- inkscape:cx="26.571947"
- inkscape:cy="80.637848"
- inkscape:window-x="0"
- inkscape:window-y="27"
+ inkscape:cx="-17.332527"
+ inkscape:cy="66.335551"
+ inkscape:window-x="1920"
+ inkscape:window-y="0"
inkscape:window-maximized="1"
- inkscape:current-layer="svg3396">
+ inkscape:current-layer="svg3396"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0">
+ id="grid3510"
+ originx="-17"
+ originy="-14" />
01
+ transform="translate(-17,-9)" />
diff --git a/xwords4/android/img_src/back.svg b/xwords4/android/img_src/back.svg
deleted file mode 100644
index d7f1cce27..000000000
--- a/xwords4/android/img_src/back.svg
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/xwords4/android/img_src/content_copy.svg b/xwords4/android/img_src/content_copy.svg
index 9992ca6ef..61b7bdc71 100644
--- a/xwords4/android/img_src/content_copy.svg
+++ b/xwords4/android/img_src/content_copy.svg
@@ -1,9 +1,49 @@
-
-
-
-
+image/svg+xml
-
-
+ transform="matrix(1.25,0,0,-1.25,-22.4875,105.0125)">
\ No newline at end of file
diff --git a/xwords4/android/img_src/content_discard.svg b/xwords4/android/img_src/content_discard.svg
index bb781e9bd..2465727ae 100644
--- a/xwords4/android/img_src/content_discard.svg
+++ b/xwords4/android/img_src/content_discard.svg
@@ -1,18 +1,59 @@
-
-
-
-
+image/svg+xml
-
-
-
-
-
-
-
-
-
-
-
+ transform="matrix(1.25,0,0,-1.25,-30.25,102.6125)">
\ No newline at end of file
diff --git a/xwords4/android/img_src/untrade.svg b/xwords4/android/img_src/untrade.svg
new file mode 100644
index 000000000..c4d051c1f
--- /dev/null
+++ b/xwords4/android/img_src/untrade.svg
@@ -0,0 +1,59 @@
+
+image/svg+xml
\ No newline at end of file
diff --git a/xwords4/android/jni/xportwrapper.c b/xwords4/android/jni/xportwrapper.c
index 375069949..8071aead2 100644
--- a/xwords4/android/jni/xportwrapper.c
+++ b/xwords4/android/jni/xportwrapper.c
@@ -95,19 +95,8 @@ and_xport_send( const XP_U8* buf, XP_U16 len, const XP_UCHAR* msgNo,
}
static void
-and_xport_relayStatus( void* closure, CommsRelayState newState )
+and_xport_relayStatus( void* XP_UNUSED(closure), CommsRelayState XP_UNUSED(newState) )
{
- AndTransportProcs* aprocs = (AndTransportProcs*)closure;
- if ( NULL != aprocs->jxport ) {
- JNIEnv* env = ENVFORME( aprocs->ti );
- const char* sig = "(L" PKG_PATH("jni/TransportProcs$CommsRelayState") ";)V";
- jmethodID mid = getMethodID( env, aprocs->jxport, "relayStatus", sig );
-
- jobject jenum = intToJEnum( env, newState,
- PKG_PATH("jni/TransportProcs$CommsRelayState") );
- (*env)->CallVoidMethod( env, aprocs->jxport, mid, jenum );
- deleteLocalRef( env, jenum );
- }
}
static void
diff --git a/xwords4/android/jni/xwjni.c b/xwords4/android/jni/xwjni.c
index 9d09568a9..fb5996244 100644
--- a/xwords4/android/jni/xwjni.c
+++ b/xwords4/android/jni/xwjni.c
@@ -1673,8 +1673,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1receiveMessage
{
jboolean result;
XWJNI_START_GLOBALS();
- XP_ASSERT( state->game.comms );
- XP_ASSERT( state->game.server );
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, globals->vtMgr,
jstream );
@@ -1686,31 +1684,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1receiveMessage
addrp = &addr;
}
- /* pthread_mutex_lock( &state->msgMutex ); */
-
- ServerCtxt* server = state->game.server;
- CommsMsgState commsState;
- result = comms_checkIncomingStream( state->game.comms, stream, addrp,
- &commsState );
- if ( result ) {
- (void)server_do( server );
-
- result = server_receiveMessage( server, stream );
- }
- comms_msgProcessed( state->game.comms, &commsState, !result );
-
- /* pthread_mutex_unlock( &state->msgMutex ); */
-
- if ( result ) {
- /* in case MORE work's pending. Multiple calls are required in at
- least one case, where I'm a host handling client registration *AND*
- I'm a robot. Only one server_do and I'll never make that first
- robot move. That's because comms can't detect a duplicate initial
- packet (in validateInitialMessage()). */
- for ( int ii = 0; ii < 5; ++ii ) {
- (void)server_do( server );
- }
- }
+ result = game_receiveMessage( &state->game, stream, addrp );
stream_destroy( stream );
diff --git a/xwords4/common/board.c b/xwords4/common/board.c
index 6e6285d1b..f697d3eb3 100644
--- a/xwords4/common/board.c
+++ b/xwords4/common/board.c
@@ -2128,13 +2128,13 @@ board_requestHint( BoardCtxt* board,
result = nTiles > 0;
}
+ XP_Bool canMove = XP_FALSE;
if ( result ) {
#ifdef XWFEATURE_SEARCHLIMIT
BdHintLimits limits;
BdHintLimits* lp = NULL;
#endif
XP_Bool wasVisible;
- XP_Bool canMove;
wasVisible = setArrowVisible( board, XP_FALSE );
@@ -2194,11 +2194,9 @@ board_requestHint( BoardCtxt* board,
}
setArrowVisible( board, wasVisible );
}
- } else {
- util_userError( board->util, ERR_NO_HINT_FOUND );
}
- if ( !result ) {
+ if ( !canMove ) {
util_userError( board->util, ERR_NO_HINT_FOUND );
}
}
diff --git a/xwords4/common/engine.c b/xwords4/common/engine.c
index b1595acac..c1e037fbe 100644
--- a/xwords4/common/engine.c
+++ b/xwords4/common/engine.c
@@ -528,7 +528,7 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
newMove->nTiles = 0;
canMove = XP_FALSE;
}
- result = XP_TRUE;
+ XP_ASSERT( result );
}
util_engineStopping( engine->util );
diff --git a/xwords4/linux/Makefile b/xwords4/linux/Makefile
index 30d0c59dc..801a2e8e2 100644
--- a/xwords4/linux/Makefile
+++ b/xwords4/linux/Makefile
@@ -265,6 +265,8 @@ REQUIRED_DEBS = gcc libgtk-3-dev \
libncursesw5-dev \
uuid-dev \
libsqlite3-dev \
+ libcurl4-openssl-dev \
+ libjson-c-dev \
.PHONY: debcheck debs_install