Merge branch 'android_branch' into android_translate

This commit is contained in:
Eric House 2018-01-17 20:54:49 -08:00
commit efec6aba66
28 changed files with 686 additions and 497 deletions

View file

@ -1,29 +1,23 @@
Here's how I'm building crosswords for Android. (Updated Dec 2017)
First, cd into the directory xwords4/android/XWords4. Everything Here's how I'm building CrossWords for Android.
First, cd into the directory xwords4/android. Everything
happens there. happens there.
IF (and it's a big if) you have all the necessary tools installed (the To build and install the debug version of CrossWords:
Android SDK and NDK, apache ant, and probably other stuff I've
forgotten), two commands will build it, the first a one-time deal and
the second repeated every time you made a change.
The build process requires a file called local.properties that must be # ./gradlew clean insXw4Deb
generated locally. Generate it before your first build by running
# ../scripts/setup_local_props.sh To build and install the debug version of CrossDbg (a variant meant
for development that can co-exist with CrossWords):
Then build using this command: # ./gradlew -PuseCrashlytics insXw4dDeb
# ant debug I do all development on Debian and Ubuntu Linux systems. I have built
on MacOS, where once you get all the necessary tools installed via
Or, if you have a device or emulator attached (listed by 'adb homebrew there's only one problem I'm aware of: the parameter 'white'
devices'), try that's passed to convert by android/scripts/images.mk on Linux systems
needs to be 'black' on MacOS. I have no clue why. If you don't make
# ant debug install this change the subset of actionbar icons that are generated from .svg
files will be black-on-black.
Making a release build requires that you've set up your keys. (I did
this too long ago to remember how but the info's easy to find). Once
that's done, just tether your device and type
# ant release install

View file

@ -1,6 +1,6 @@
def INITIAL_CLIENT_VERS = 8 def INITIAL_CLIENT_VERS = 8
def VERSION_CODE_BASE = 127 def VERSION_CODE_BASE = 128
def VERSION_NAME = '4.4.131' def VERSION_NAME = '4.4.132'
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY") def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
def GCM_SENDER_ID = System.getenv("GCM_SENDER_ID") def GCM_SENDER_ID = System.getenv("GCM_SENDER_ID")
def BUILD_INFO_NAME = "build-info.txt" def BUILD_INFO_NAME = "build-info.txt"
@ -201,6 +201,8 @@ dependencies {
compile 'com.android.support:support-v4:23.4.0' compile 'com.android.support:support-v4:23.4.0'
// 2.6.8 is probably as far forward as I can go without upping my
// min-supported SDK version
xw4dCompile('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') { xw4dCompile('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
transitive = true; transitive = true;
} }

View file

@ -13,9 +13,9 @@
</style> </style>
</head> </head>
<body> <body>
<h2>CrossWords 4.4.131 release</h2> <h2>CrossWords 4.4.132 release</h2>
<p>An F-Droid-only release meeting new requirements</p> <p>This release makes communication with the relay more robust.</p>
<div id="survey"> <div id="survey">
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take <p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
@ -25,10 +25,14 @@
<h3>New with this release</h3> <h3>New with this release</h3>
<ul> <ul>
<li>F-Droid has stiffened their prohibition against including <li>Communicate with relay via http when the custom protocol
proprietary Google components. This release complies by removing isn't working (e.g. when a wifi router or firewall blocks
&quot;Google Cloud Messaging&quot; which never worked on it.)</li>
f-droid installs anyway.</li> <li>Improved translations for French, Japanese and Norwegian
(by Weblate volunteers)</li>
<li>Add a few menubar icons (thanks to The Noun Project)</li>
<li>Fix taps on left end of items in the Games List not always
selecting/unselecting them</li>
</ul> </ul>
<p>(The full changelog <p>(The full changelog

View file

@ -387,32 +387,6 @@ public class CommsTransport implements TransportProcs,
return nSent; 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, public void relayConnd( String room, int devOrder, boolean allHere,
int nMissing ) int nMissing )
{ {

View file

@ -482,14 +482,12 @@ public class GameUtils {
if ( force ) { if ( force ) {
HashMap<Long,CommsConnTypeSet> games = HashMap<Long,CommsConnTypeSet> games =
DBUtils.getGamesWithSendsPending( context ); 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 */ System.arraycopy( sendTimes, 0, /* src */
sendTimes, 1, /* dest */ sendTimes, 1, /* dest */
sendTimes.length - 1 ); sendTimes.length - 1 );
sendTimes[0] = now; sendTimes[0] = now;
}
} }
} }
@ -1259,7 +1257,7 @@ public class GameUtils {
private HashMap<Long,CommsConnTypeSet> m_games; private HashMap<Long,CommsConnTypeSet> m_games;
private ResendDoneProc m_doneProc; private ResendDoneProc m_doneProc;
private CommsConnType m_filter; private CommsConnType m_filter;
private MultiMsgSink m_sink; private int m_nSent = 0;
public ResendTask( Context context, HashMap<Long,CommsConnTypeSet> games, public ResendTask( Context context, HashMap<Long,CommsConnTypeSet> games,
CommsConnType filter, ResendDoneProc proc ) CommsConnType filter, ResendDoneProc proc )
@ -1288,14 +1286,15 @@ public class GameUtils {
GameLock lock = new GameLock( rowid, false ); GameLock lock = new GameLock( rowid, false );
if ( lock.tryLock() ) { if ( lock.tryLock() ) {
CurGameInfo gi = new CurGameInfo( m_context ); CurGameInfo gi = new CurGameInfo( m_context );
m_sink = new MultiMsgSink( m_context, rowid ); MultiMsgSink sink = new MultiMsgSink( m_context, rowid );
GamePtr gamePtr = loadMakeGame( m_context, gi, m_sink, lock ); GamePtr gamePtr = loadMakeGame( m_context, gi, sink, lock );
if ( null != gamePtr ) { if ( null != gamePtr ) {
int nSent = XwJNI.comms_resendAll( gamePtr, true, int nSent = XwJNI.comms_resendAll( gamePtr, true,
m_filter, false ); m_filter, false );
gamePtr.release(); gamePtr.release();
Log.d( TAG, "ResendTask.doInBackground(): sent %d " Log.d( TAG, "ResendTask.doInBackground(): sent %d "
+ "messages for rowid %d", nSent, rowid ); + "messages for rowid %d", nSent, rowid );
m_nSent += sink.numSent();
} else { } else {
Log.d( TAG, "ResendTask.doInBackground(): loadMakeGame()" Log.d( TAG, "ResendTask.doInBackground(): loadMakeGame()"
+ " failed for rowid %d", rowid ); + " failed for rowid %d", rowid );
@ -1320,8 +1319,7 @@ public class GameUtils {
protected void onPostExecute( Void unused ) protected void onPostExecute( Void unused )
{ {
if ( null != m_doneProc ) { if ( null != m_doneProc ) {
int nSent = null == m_sink ? 0 : m_sink.numSent(); m_doneProc.onResendDone( m_context, m_nSent );
m_doneProc.onResendDone( m_context, nSent );
} }
} }
} }

View file

@ -119,10 +119,6 @@ public class MultiMsgSink implements TransportProcs {
return nSent; return nSent;
} }
public void relayStatus( CommsRelayState newState )
{
}
public void relayErrorProc( XWRELAY_ERROR relayErr ) public void relayErrorProc( XWRELAY_ERROR relayErr )
{ {
} }

View file

@ -60,7 +60,9 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class RelayService extends XWService public class RelayService extends XWService
implements NetStateCache.StateChangedIf { 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 MAX_BUF = MAX_SEND - 2;
private static final int REG_WAIT_INTERVAL = 10; private static final int REG_WAIT_INTERVAL = 10;
private static final int INITIAL_BACKOFF = 5; private static final int INITIAL_BACKOFF = 5;
private static final int UDP_FAIL_LIMIT = 5;
// One day, in seconds. Probably should be configurable. // One day, in seconds. Probably should be configurable.
private static final long MAX_KEEPALIVE_SECS = 24 * 60 * 60; 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 ROWID = "ROWID";
private static final String BINBUFFER = "BINBUFFER"; private static final String BINBUFFER = "BINBUFFER";
private static Map<Integer, PacketData> s_packetsSentUDP = new HashMap<>(); private static List<PacketData> s_packetsSentUDP = new ArrayList<>();
private static Map<Integer, PacketData> s_packetsSentWeb = new HashMap<>(); private static List<PacketData> s_packetsSentWeb = new ArrayList<>();
private static AtomicInteger s_nextPacketID = new AtomicInteger(); private static AtomicInteger s_nextPacketID = new AtomicInteger();
private static boolean s_gcmWorking = false; private static boolean s_gcmWorking = false;
private static boolean s_registered = false; private static boolean s_registered = false;
@ -110,18 +113,14 @@ public class RelayService extends XWService
private static long s_curNextTimer; private static long s_curNextTimer;
static { resetBackoffTimer(); } static { resetBackoffTimer(); }
private Thread m_fetchThread = null; private Thread m_fetchThread = null; // no longer used
private Thread m_UDPReadThread = null; private AtomicReference<UDPThreads> m_UDPThreadsRef = new AtomicReference<>();
private Thread m_UDPWriteThread = null;
private DatagramSocket m_UDPSocket;
private LinkedBlockingQueue<PacketData> m_queue =
new LinkedBlockingQueue<PacketData>();
private Handler m_handler; private Handler m_handler;
private Runnable m_onInactivity; private Runnable m_onInactivity;
private int m_maxIntervalSeconds = 0; private int m_maxIntervalSeconds = 0;
private long m_lastGamePacketReceived; private long m_lastGamePacketReceived;
// m_nativeNotWorking: set to true if too many acks missed? private int m_nativeFailScore;
private boolean m_nativeNotWorking = false; private boolean m_skipUPDSet;
private static DevIDType s_curType = DevIDType.ID_TYPE_NONE; private static DevIDType s_curType = DevIDType.ID_TYPE_NONE;
private static long s_regStartTime = 0; private static long s_regStartTime = 0;
@ -318,7 +317,7 @@ public class RelayService extends XWService
// Exists to get incoming data onto the main thread // Exists to get incoming data onto the main thread
private static void postData( Context context, long rowid, byte[] msg ) 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 ); msg.length, rowid );
if ( DBUtils.haveGame( context, rowid ) ) { if ( DBUtils.haveGame( context, rowid ) ) {
Intent intent = getIntentTo( context, MsgCmds.RECEIVE ) Intent intent = getIntentTo( context, MsgCmds.RECEIVE )
@ -386,6 +385,8 @@ public class RelayService extends XWService
} }
} }
}; };
m_skipUPDSet = XWPrefs.getSkipToWebAPI( this );
} }
@Override @Override
@ -400,7 +401,7 @@ public class RelayService extends XWService
cmd = null; cmd = null;
} }
if ( null != cmd ) { if ( null != cmd ) {
Log.d( TAG, "onStartCommand(): cmd=%s", cmd.toString() ); // Log.d( TAG, "onStartCommand(): cmd=%s", cmd.toString() );
switch( cmd ) { switch( cmd ) {
case PROCESS_GAME_MSGS: case PROCESS_GAME_MSGS:
String[] relayIDs = new String[1]; String[] relayIDs = new String[1];
@ -554,278 +555,64 @@ public class RelayService extends XWService
private void startUDPThreadsIfNot() private void startUDPThreadsIfNot()
{ {
if ( XWApp.UDP_ENABLED && relayEnabled( this ) ) { if ( XWApp.UDP_ENABLED && relayEnabled( this ) ) {
if ( null == m_UDPReadThread ) { synchronized ( m_UDPThreadsRef ) {
m_UDPReadThread = new Thread( null, new Runnable() { if ( null == m_UDPThreadsRef.get() ) {
public void run() { UDPThreads threads = new UDPThreads();
m_UDPThreadsRef.set( threads );
connectSocket(); // block until this is done threads.start();
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" );
} }
} else { } else {
Log.i( TAG, "startUDPThreadsIfNot(): UDP disabled" ); Log.i( TAG, "startUDPThreadsIfNot(): UDP disabled" );
} }
} // startUDPThreadsIfNot } // 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() private boolean skipNativeSend()
{ {
boolean skip = m_nativeNotWorking; boolean skip = m_nativeFailScore > UDP_FAIL_LIMIT || m_skipUPDSet;
if ( ! skip ) { // Log.d( TAG, "skipNativeSend(score=%d)) => %b", m_nativeFailScore, skip );
skip = XWPrefs.getSkipToWebAPI( RelayService.this );
}
return 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 ) { Log.d( TAG, "Sent (fromUDP=%b) packet: cmd=%s, id=%d",
m_UDPWriteThread = new Thread( null, new Runnable() { fromUDP, packet.m_cmd.toString(), packet.m_packetID );
public void run() { if ( fromUDP || packet.m_cmd != XWRelayReg.XWPDEV_ACK ) {
Log.i( TAG, "write thread starting" ); List<PacketData> list = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
for ( ; ; ) { synchronized( list ) {
boolean exitNow = false; list.add(packet );
boolean useWeb = skipNativeSend();
List<PacketData> dataListUDP = new ArrayList<>();
List<PacketData> 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<PacketData> 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 );
}
} }
} }
return sentLen;
} }
private int sendViaUDP( List<PacketData> packets ) private void noteSent( List<PacketData> packets, boolean fromUDP )
{ {
int sentLen = 0; long nowMS = System.currentTimeMillis();
List<PacketData> 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 ) { for ( PacketData packet : packets ) {
boolean getOut = true; if ( fromUDP ) {
byte[] data = packet.assemble(); packet.setSentMS( nowMS );
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;
} }
noteSent( packet, fromUDP );
} }
Log.d( TAG, "noteSent(fromUDP=%b): size after: %d", fromUDP, map.size() );
if ( sentLen > 0 ) {
startAckTimer( packets );
}
return sentLen;
}
private void startAckTimer( final List<PacketData> packets )
{
Runnable ackTimer = new Runnable() {
@Override
public void run() {
List<PacketData> 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<Integer, PacketData> 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<PacketData> packets, Map<Integer, PacketData> map )
{
for ( PacketData packet : packets ) {
noteSent( packet, map );
}
} }
private void stopUDPThreadsIf() private void stopUDPThreadsIf()
{ {
DbgUtils.assertOnUIThread(); DbgUtils.assertOnUIThread();
if ( null != m_UDPWriteThread ) { UDPThreads threads = m_UDPThreadsRef.getAndSet( null );
// can't add null if ( null != threads ) {
m_queue.add( new PacketData() ); threads.stop();
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;
} }
} }
@ -928,7 +715,7 @@ public class RelayService extends XWService
if ( resetBackoff ) { if ( resetBackoff ) {
resetBackoffTimer(); resetBackoffTimer();
} }
} } // gotPacket()
private void gotPacket( DatagramPacket packet ) private void gotPacket( DatagramPacket packet )
{ {
@ -1014,14 +801,14 @@ public class RelayService extends XWService
private void requestMessagesImpl( XWRelayReg reg ) private void requestMessagesImpl( XWRelayReg reg )
{ {
ByteArrayOutputStream bas = new ByteArrayOutputStream();
try { try {
DevIDType[] typp = new DevIDType[1]; DevIDType[] typp = new DevIDType[1];
String devid = getDevID( typp ); String devid = getDevID( typp );
if ( null != devid ) { if ( null != devid ) {
ByteArrayOutputStream bas = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream( bas ); DataOutputStream out = new DataOutputStream( bas );
writeVLIString( out, devid ); 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 ); postPacket( bas, reg );
} else { } else {
Log.d(TAG, "requestMessagesImpl(): devid is null" ); Log.d(TAG, "requestMessagesImpl(): devid is null" );
@ -1149,7 +936,11 @@ public class RelayService extends XWService
private void postPacket( ByteArrayOutputStream bas, XWRelayReg cmd ) 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! // 0 ok; thread will often have sent already!
// DbgUtils.logf( "postPacket() done; %d in queue", m_queue.size() ); // 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<PacketData> m_queue =
new LinkedBlockingQueue<PacketData>();
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<PacketData> dataListUDP = new ArrayList<>();
List<PacketData> 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<PacketData> 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<PacketData> 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<PacketData> forResend = new ArrayList<>();
boolean foundNonAck = false;
synchronized ( s_packetsSentUDP ) {
Iterator<PacketData> 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<Void, Void, Void> { private static class AsyncSender extends AsyncTask<Void, Void, Void> {
private Context m_context; private Context m_context;
private HashMap<String,ArrayList<byte[]>> m_msgHash; private HashMap<String,ArrayList<byte[]>> m_msgHash;
@ -1227,7 +1289,6 @@ public class RelayService extends XWService
@Override @Override
protected Void doInBackground( Void... ignored ) protected Void doInBackground( Void... ignored )
{ {
Assert.assertFalse( XWPrefs.getSkipToWebAPI( m_context ) );
// format: total msg lenth: 2 // format: total msg lenth: 2
// number-of-relayIDs: 2 // number-of-relayIDs: 2
// for-each-relayid: relayid + '\n': varies // 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 // Now open a real socket, write size and proto, and
// copy in the formatted buffer // copy in the formatted buffer
Assert.assertFalse( XWPrefs.getSkipToWebAPI( m_context ) );
Socket socket = NetUtils.makeProxySocket( m_context, 8000 ); Socket socket = NetUtils.makeProxySocket( m_context, 8000 );
if ( null != socket ) { if ( null != socket ) {
DataOutputStream outStream = DataOutputStream outStream =
@ -1358,27 +1418,47 @@ public class RelayService extends XWService
return nextPacketID; return nextPacketID;
} }
private static void noteAck( int packetID, boolean fromUDP ) private void noteAck( int packetID, boolean fromUDP )
{ {
PacketData packet; Assert.assertTrue( packetID != 0 );
Map<Integer, PacketData> map = fromUDP ? s_packetsSentUDP : s_packetsSentWeb; List<PacketData> map = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
synchronized( map ) { synchronized( map ) {
packet = map.remove( packetID ); PacketData packet = null;
Iterator<PacketData> 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 ) { if ( packet != null ) {
Log.d( TAG, "noteAck(fromUDP=%b): removed for id %d: %s", // Log.d( TAG, "noteAck(fromUDP=%b): removed for id %d: %s",
fromUDP, packetID, packet ); // fromUDP, packetID, packet );
if ( fromUDP ) {
--m_nativeFailScore;
}
} else { } else {
Log.w( TAG, "Weird: got ack %d but never sent", packetID ); Log.w( TAG, "Weird: got ack %d but never sent", packetID );
} }
if ( BuildConfig.DEBUG ) { if ( BuildConfig.DEBUG ) {
ArrayList<String> pstrs = new ArrayList<>(); ArrayList<String> pstrs = new ArrayList<>();
for ( Integer pkid : map.keySet() ) { for ( PacketData datum : map ) {
pstrs.add( map.get(pkid).toString() ); pstrs.add( String.format("%d", datum.m_packetID ) );
} }
Log.d( TAG, "noteAck(fromUDP=%b): Got ack for %d; there are %d unacked packets: %s", Log.d( TAG, "noteAck(fromUDP=%b): Got ack for %d; there are %d unacked packets: %s",
fromUDP, packetID, map.size(), TextUtils.join( ",", pstrs ) ); 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 // Called from any thread
@ -1490,7 +1570,7 @@ public class RelayService extends XWService
result = figureBackoffSeconds(); result = figureBackoffSeconds();
} }
Log.d( TAG, "getMaxIntervalSeconds() => %d", result ); Log.d( TAG, "getMaxIntervalSeconds() => %d", result ); // WFT? went from 40 to 1000
return result; return result;
} }
@ -1526,7 +1606,8 @@ public class RelayService extends XWService
private boolean shouldMaintainConnection() private boolean shouldMaintainConnection()
{ {
boolean result = relayEnabled( this ) boolean result = relayEnabled( this )
&& (XWApp.GCM_IGNORED || !s_gcmWorking); && (!s_gcmWorking || XWPrefs.getIgnoreGCM( this ));
if ( result ) { if ( result ) {
long interval = Utils.getCurSeconds() - m_lastGamePacketReceived; long interval = Utils.getCurSeconds() - m_lastGamePacketReceived;
result = interval < MAX_KEEPALIVE_SECS; result = interval < MAX_KEEPALIVE_SECS;
@ -1564,7 +1645,7 @@ public class RelayService extends XWService
Assert.assertTrue( diff < Integer.MAX_VALUE ); Assert.assertTrue( diff < Integer.MAX_VALUE );
result = (int)diff; result = (int)diff;
} }
Log.d( TAG, "figureBackoffSeconds() => %d", result ); // Log.d( TAG, "figureBackoffSeconds() => %d", result );
return result; return result;
} }
@ -1583,16 +1664,12 @@ public class RelayService extends XWService
public byte[] m_header; public byte[] m_header;
public int m_packetID; public int m_packetID;
private long m_created; private long m_created;
private boolean m_useWeb; private long m_sentUDP;
public PacketData() { private PacketData() {}
m_bas = null;
m_created = System.currentTimeMillis();
}
public PacketData( ByteArrayOutputStream bas, XWRelayReg cmd ) public PacketData( ByteArrayOutputStream bas, XWRelayReg cmd )
{ {
this();
m_bas = bas; m_bas = bas;
m_cmd = cmd; m_cmd = cmd;
} }
@ -1604,10 +1681,9 @@ public class RelayService extends XWService
System.currentTimeMillis() - m_created ); System.currentTimeMillis() - m_created );
} }
void setForWeb() { m_useWeb = true; } void setSentMS( long ms ) { m_sentUDP = ms; }
boolean getForWeb() { return m_useWeb; } long getSentMS() { return m_sentUDP; }
boolean getForWeb() { return m_sentUDP != 0; }
public boolean isEOQ() { return 0 == getLength(); }
public int getLength() 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 {}
} }

View file

@ -443,7 +443,7 @@ public class Utils {
{ {
// Note: an int is big enough for *seconds* (not milliseconds) since 1970 // Note: an int is big enough for *seconds* (not milliseconds) since 1970
// until 2038 // until 2038
long millis = new Date().getTime(); long millis = System.currentTimeMillis();
int result = (int)(millis / 1000); int result = (int)(millis / 1000);
return result; return result;
} }

View file

@ -39,7 +39,6 @@ public class XWApp extends Application {
public static final boolean ATTACH_SUPPORTED = false; public static final boolean ATTACH_SUPPORTED = false;
public static final boolean LOG_LIFECYLE = false; public static final boolean LOG_LIFECYLE = false;
public static final boolean DEBUG_EXP_TIMERS = 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 UDP_ENABLED = true;
public static final boolean SMS_INVITE_ENABLED = true; public static final boolean SMS_INVITE_ENABLED = true;
public static final boolean LOCUTILS_ENABLED = false; public static final boolean LOCUTILS_ENABLED = false;

View file

@ -67,6 +67,16 @@ public class XWPrefs {
return getPrefsBoolean( context, R.string.key_enable_nfc_toself, false ); 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 ) public static boolean getRelayInviteToSelfEnabled( Context context )
{ {
return getPrefsBoolean( context, R.string.key_enable_relay_toself, false ); return getPrefsBoolean( context, R.string.key_enable_relay_toself, false );

View file

@ -266,15 +266,19 @@ public class JNIThread extends Thread {
} }
public boolean busy() public boolean busy()
{ // synchronize this!!! {
boolean result = false; 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<QueueElem> iter = m_queue.iterator(); Iterator<QueueElem> iter = m_queue.iterator();
while ( iter.hasNext() ) { while ( iter.hasNext() && !result ) {
if ( iter.next().m_isUIEvent ) { result = iter.next().m_isUIEvent;
result = true;
break;
}
} }
return result; return result;
} }

View file

@ -38,7 +38,6 @@ public interface TransportProcs {
, COMMS_RELAYSTATE_RECONNECTED , COMMS_RELAYSTATE_RECONNECTED
, COMMS_RELAYSTATE_ALLCONNECTED , COMMS_RELAYSTATE_ALLCONNECTED
}; };
void relayStatus( CommsRelayState newState );
void relayConnd( String room, int devOrder, boolean allHere, int nMissing ); void relayConnd( String room, int devOrder, boolean allHere, int nMissing );

View file

@ -32,7 +32,7 @@
<item android:id="@+id/board_menu_trade_cancel" <item android:id="@+id/board_menu_trade_cancel"
android:title="@string/button_trade_cancel" android:title="@string/button_trade_cancel"
android:showAsAction="ifRoom" android:showAsAction="ifRoom"
android:icon="@drawable/back__gen" android:icon="@drawable/untrade__gen"
/> />
<item android:id="@+id/board_menu_trade_commit" <item android:id="@+id/board_menu_trade_commit"

View file

@ -126,6 +126,8 @@
<string name="key_enable_nfc_toself">key_enable_nfc_toself</string> <string name="key_enable_nfc_toself">key_enable_nfc_toself</string>
<string name="key_enable_sms_toself">key_enable_sms_toself</string> <string name="key_enable_sms_toself">key_enable_sms_toself</string>
<string name="key_enable_relay_toself">key_enable_relay_toself</string> <string name="key_enable_relay_toself">key_enable_relay_toself</string>
<string name="key_ignore_gcm">key_ignore_gcm</string>
<string name="key_show_gcm">key_show_gcm</string>
<string name="key_nag_intervals">key_nag_intervals</string> <string name="key_nag_intervals">key_nag_intervals</string>
<string name="key_download_path">key_download_path</string> <string name="key_download_path">key_download_path</string>
<string name="key_got_langdict">key_got_langdict</string> <string name="key_got_langdict">key_got_langdict</string>

View file

@ -1692,7 +1692,7 @@
<string name="about_vers_fmt">CrossWords for Android, Version %1$s, <string name="about_vers_fmt">CrossWords for Android, Version %1$s,
rev %2$s, built on %3$s.</string> rev %2$s, built on %3$s.</string>
<!-- copyright info --> <!-- copyright info -->
<string name="about_copyright">Copyright (C) 1998-2017 by Eric <string name="about_copyright">Copyright (C) 1998-2018 by Eric
House. This free/open source software is released under the GNU Public House. This free/open source software is released under the GNU Public
License.</string> License.</string>
@ -2601,6 +2601,12 @@
<string name="enable_relay_toself_title">Enable relay invites to self</string> <string name="enable_relay_toself_title">Enable relay invites to self</string>
<string name="enable_relay_toself_summary">(To aid testing and debugging)</string> <string name="enable_relay_toself_summary">(To aid testing and debugging)</string>
<string name="ignore_gcm_title">Ignore incoming GCM messages</string>
<string name="ignore_gcm_summary">Mimic life without a google account</string>
<string name="show_sms_title">Show SMS sends, receives</string>
<string name="show_gcm_title">Show GCM receives</string>
<!-- Shown after "resend messages" menuitem chosen --> <!-- Shown after "resend messages" menuitem chosen -->
<plurals name="resent_msgs_fmt"> <plurals name="resent_msgs_fmt">
<item quantity="one">One move sent</item> <item quantity="one">One move sent</item>

View file

@ -390,28 +390,6 @@
android:defaultValue="false" android:defaultValue="false"
/> />
<PreferenceScreen android:title="@string/pref_group_sms_title"
android:summary="@string/pref_group_sms_summary"
>
<CheckBoxPreference android:key="@string/key_enable_sms_toself"
android:title="@string/enable_sms_toself_title"
android:summary="@string/enable_sms_toself_summary"
android:defaultValue="false"
/>
<org.eehouse.android.xw4.XWListPreference
android:key="@string/key_force_radio"
android:title="@string/force_radio_title"
android:entries="@array/force_radio_names"
android:entryValues="@array/force_radio_names"
android:defaultValue="@string/radio_name_real"
/>
<CheckBoxPreference android:key="@string/key_show_sms"
android:title="Show SMS sends, receives"
android:defaultValue="false"
/>
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_group_relay_title" <PreferenceScreen android:title="@string/pref_group_relay_title"
android:summary="@string/pref_group_relay_summary" android:summary="@string/pref_group_relay_summary"
> >
@ -420,6 +398,15 @@
android:summary="@string/enable_relay_toself_summary" android:summary="@string/enable_relay_toself_summary"
android:defaultValue="false" android:defaultValue="false"
/> />
<CheckBoxPreference android:key="@string/key_ignore_gcm"
android:title="@string/ignore_gcm_title"
android:summary="@string/ignore_gcm_summary"
android:defaultValue="false"
/>
<CheckBoxPreference android:key="@string/key_show_gcm"
android:title="@string/show_gcm_title"
android:defaultValue="false"
/>
<org.eehouse.android.xw4.XWEditTextPreference <org.eehouse.android.xw4.XWEditTextPreference
android:key="@string/key_relay_host" android:key="@string/key_relay_host"
android:title="@string/relay_host" android:title="@string/relay_host"
@ -452,6 +439,28 @@
/> />
</PreferenceScreen> </PreferenceScreen>
<PreferenceScreen android:title="@string/pref_group_sms_title"
android:summary="@string/pref_group_sms_summary"
>
<CheckBoxPreference android:key="@string/key_enable_sms_toself"
android:title="@string/enable_sms_toself_title"
android:summary="@string/enable_sms_toself_summary"
android:defaultValue="false"
/>
<org.eehouse.android.xw4.XWListPreference
android:key="@string/key_force_radio"
android:title="@string/force_radio_title"
android:entries="@array/force_radio_names"
android:entryValues="@array/force_radio_names"
android:defaultValue="@string/radio_name_real"
/>
<CheckBoxPreference android:key="@string/key_show_sms"
android:title="@string/show_sms_title"
android:defaultValue="false"
/>
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_group_l10n_title" <PreferenceScreen android:title="@string/pref_group_l10n_title"
android:summary="@string/pref_group_l10n_summary" android:summary="@string/pref_group_l10n_summary"
> >

View file

@ -34,6 +34,8 @@ import junit.framework.Assert;
public class GCMIntentService extends GCMBaseIntentService { public class GCMIntentService extends GCMBaseIntentService {
private static final String TAG = GCMIntentService.class.getSimpleName(); private static final String TAG = GCMIntentService.class.getSimpleName();
private Boolean m_toastGCM;
public GCMIntentService() public GCMIntentService()
{ {
super( BuildConfig.GCM_SENDER_ID ); super( BuildConfig.GCM_SENDER_ID );
@ -67,14 +69,19 @@ public class GCMIntentService extends GCMBaseIntentService {
protected void onMessage( Context context, Intent intent ) protected void onMessage( Context context, Intent intent )
{ {
Log.d( TAG, "onMessage()" ); Log.d( TAG, "onMessage()" );
notifyRelayService( context, true );
String value; if ( null == m_toastGCM ) {
boolean ignoreIt = XWApp.GCM_IGNORED; m_toastGCM = new Boolean( XWPrefs.getToastGCM( context ) );
if ( ignoreIt ) { }
Log.d( TAG, "received GCM but ignoring it" );
if ( XWPrefs.getIgnoreGCM( context ) ) {
String logMsg = "received GCM but ignoring it";
Log.d( TAG, logMsg );
DbgUtils.showf( context, logMsg );
} else { } else {
value = intent.getStringExtra( "checkUpdates" ); notifyRelayService( context, true );
String value = intent.getStringExtra( "checkUpdates" );
if ( null != value && Boolean.parseBoolean( value ) ) { if ( null != value && Boolean.parseBoolean( value ) ) {
UpdateCheckReceiver.checkVersions( context, true ); UpdateCheckReceiver.checkVersions( context, true );
} }
@ -82,6 +89,9 @@ public class GCMIntentService extends GCMBaseIntentService {
value = intent.getStringExtra( "getMoves" ); value = intent.getStringExtra( "getMoves" );
if ( null != value && Boolean.parseBoolean( value ) ) { if ( null != value && Boolean.parseBoolean( value ) ) {
RelayService.timerFired( context ); RelayService.timerFired( context );
if ( m_toastGCM ) {
DbgUtils.showf( context, "onMessage(): got 'getMoves'" );
}
} }
value = intent.getStringExtra( "msgs64" ); value = intent.getStringExtra( "msgs64" );
@ -90,6 +100,11 @@ public class GCMIntentService extends GCMBaseIntentService {
try { try {
JSONArray msgs64 = new JSONArray( value ); JSONArray msgs64 = new JSONArray( value );
String[] strs64 = new String[msgs64.length()]; 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 ) { for ( int ii = 0; ii < strs64.length; ++ii ) {
strs64[ii] = msgs64.optString(ii); strs64[ii] = msgs64.optString(ii);
} }
@ -100,6 +115,7 @@ public class GCMIntentService extends GCMBaseIntentService {
} }
} catch (org.json.JSONException jse ) { } catch (org.json.JSONException jse ) {
Log.ex( TAG, 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 ) private void notifyRelayService( Context context, boolean working )
{ {
if ( working && XWApp.GCM_IGNORED ) { if ( !XWPrefs.getIgnoreGCM( context ) ) {
working = false; RelayService.gcmConfirmed( context, working );
} }
RelayService.gcmConfirmed( context, working );
} }
} }

View file

@ -0,0 +1 @@
strings.xml

View file

@ -8,15 +8,15 @@
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
data-name="Layer 1" data-name="Layer 1"
viewBox="0 0 100 100" viewBox="0 0 66 82"
x="0px" x="0px"
y="0px" y="0px"
id="svg3396" id="svg3396"
version="1.1" version="1.1"
inkscape:version="0.91 r13725" inkscape:version="0.91 r13725"
sodipodi:docname="archive.svg" sodipodi:docname="archive.svg"
width="100" width="66"
height="100"> height="82">
<metadata <metadata
id="metadata3416"> id="metadata3416">
<rdf:RDF> <rdf:RDF>
@ -41,40 +41,46 @@
inkscape:pageopacity="0" inkscape:pageopacity="0"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:window-width="1920" inkscape:window-width="1920"
inkscape:window-height="1016" inkscape:window-height="1163"
id="namedview3412" id="namedview3412"
showgrid="true" showgrid="true"
showborder="false" showborder="false"
inkscape:zoom="6.616" inkscape:zoom="6.616"
inkscape:cx="26.571947" inkscape:cx="-17.332527"
inkscape:cy="80.637848" inkscape:cy="66.335551"
inkscape:window-x="0" inkscape:window-x="1920"
inkscape:window-y="27" inkscape:window-y="0"
inkscape:window-maximized="1" 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">
<inkscape:grid <inkscape:grid
type="xygrid" type="xygrid"
id="grid3510" /> id="grid3510"
originx="-17"
originy="-14" />
</sodipodi:namedview> </sodipodi:namedview>
<title <title
id="title3398">01</title> id="title3398">01</title>
<path <path
d="m 83,26 -66,0 0,60 66,0 z m -6,54 -54,0 0,-48 54,0 z" d="M 66,22 0,22 0,82 66,82 Z M 60,76 6,76 6,28 60,28 Z"
id="path3400" id="path3400"
inkscape:connector-curvature="0" /> inkscape:connector-curvature="0" />
<polygon <polygon
points="41.7,46.7 35.7,46.7 35.7,58.3 64.3,58.3 64.3,46.7 64.3,46.7 58.3,46.7 58.3,52.3 41.7,52.3 " points="64.3,58.3 64.3,46.7 64.3,46.7 58.3,46.7 58.3,52.3 41.7,52.3 41.7,46.7 35.7,46.7 35.7,58.3 "
id="polygon3402" id="polygon3402"
transform="translate(0,-5)" /> transform="translate(-17,-9)" />
<rect <rect
x="25" x="8"
y="15" y="11"
width="50" width="50"
height="6" height="6"
id="rect3404" /> id="rect3404" />
<rect <rect
x="30" x="13"
y="4" y="0"
width="40" width="40"
height="6" height="6"
id="rect3406" /> id="rect3406" />

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120"
height="120" xml:space="preserve">
<g
id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)">
<g transform='translate(46.03,16.24)' id='g1584'>
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 5.5,5.502 -16.87,27.87 35.7,27.87 35.7,35.65 -16.87,35.65 5.5,58.02 0,63.52 -31.76,31.76 0,0 z' id='path1586'/>
</g></g>
</svg>

Before

Width:  |  Height:  |  Size: 609 B

View file

@ -1,9 +1,49 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120" xmlns:dc="http://purl.org/dc/elements/1.1/"
height="120" xml:space="preserve"> xmlns:cc="http://creativecommons.org/ns#"
<g xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="75.012497"
height="90.025002"
xml:space="preserve"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="content_copy.svg"><metadata
id="metadata10"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs8" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="787"
inkscape:window-height="480"
id="namedview6"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.9666667"
inkscape:cx="37.5125"
inkscape:cy="45.0125"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" /><g
id="g12" id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)"> transform="matrix(1.25,0,0,-1.25,-22.4875,105.0125)"><path
<path style='fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none' d='M 29.99,72.01 78,72.01 78,11.99 29.99,11.99 29.99,72.01 z M 72,66 36,66 36,17.99 72,17.99 72,66 z M 55.34,53 42,53 42,55 55.34,55 55.34,53 z M 66,47 41.99,47 41.99,49 66,49 66,47 z M 59.34,41 42,41 42,43 59.34,43 59.34,41 z M 55.34,35 42,35 42,37 55.34,37 55.34,35 z M 64.67,28.99 41.99,28.99 41.99,30.99 64.67,30.99 64.67,28.99 z M 60.01,78.01 23.99,78.01 23.99,29.99 26.99,29.99 26.99,23.99 23.99,23.99 17.99,23.99 17.99,84.01 66,84.01 66,78.01 66,75.01 60.01,75.01 60.01,78.01 z' id='path1220'/></g> style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
</svg> d="m 29.99,72.01 48.01,0 0,-60.02 -48.01,0 0,60.02 z M 72,66 36,66 36,17.99 72,17.99 72,66 Z M 55.34,53 42,53 l 0,2 13.34,0 0,-2 z M 66,47 l -24.01,0 0,2 24.01,0 0,-2 z M 59.34,41 42,41 l 0,2 17.34,0 0,-2 z m -4,-6 -13.34,0 0,2 13.34,0 0,-2 z m 9.33,-6.01 -22.68,0 0,2 22.68,0 0,-2 z m -4.66,49.02 -36.02,0 0,-48.02 3,0 0,-6 -3,0 -6,0 0,60.02 48.01,0 0,-6 0,-3 -5.99,0 0,3 z"
id="path1220"
inkscape:connector-curvature="0" /></g></svg>

Before

Width:  |  Height:  |  Size: 933 B

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

@ -1,18 +1,59 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120" xmlns:dc="http://purl.org/dc/elements/1.1/"
height="120" xml:space="preserve"> xmlns:cc="http://creativecommons.org/ns#"
<g xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
width="59.5"
height="85.212502"
xml:space="preserve"
id="svg2"
inkscape:version="0.91 r13725"
sodipodi:docname="content_discard.svg"><metadata
id="metadata15"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs13" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1594"
inkscape:window-height="887"
id="namedview11"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.9666667"
inkscape:cx="18.055085"
inkscape:cy="42.6"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" /><g
id="g12" id="g12"
transform="matrix(1.25,0,0,-1.25,0,120)"> transform="matrix(1.25,0,0,-1.25,-30.25,102.6125)"><g
<g id='g1254'> id="g1254"><g
<g id='g1256'> id="g1256"><g
<g transform='translate(68.92,54.16)' id='g1262'> transform="translate(68.92,54.16)"
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 C -0.046,0.281 -0.218,0.557 -0.476,0.82 -2.555,-1.292 -10.92,-1.642 -20.92,-1.642 -30.92,-1.642 -39.28,-1.292 -41.37,0.82 -41.62,0.557 -41.78,0.281 -41.84,0 L -41.86,0 -41.86,-0.152 C -41.86,-0.17 -41.86,-0.188 -41.86,-0.206 -41.86,-0.247 -41.85,-0.283 -41.85,-0.322 L -40.22,-33.68 -40.21,-33.68 C -40.07,-36.29 -37.07,-40.24 -20.92,-40.24 -4.766,-40.24 -1.77,-36.29 -1.629,-33.68 L -1.617,-33.68 0.013,-0.322 C 0.016,-0.283 0.028,-0.247 0.028,-0.206 0.028,-0.188 0.016,-0.17 0.016,-0.152 L 0.028,0 0,0 z' id='path1264'/> id="g1262"><path
</g> style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
<g transform='translate(42.16,71.94)' id='g1266'> d="m 0,0 c -0.046,0.281 -0.218,0.557 -0.476,0.82 -2.079,-2.112 -10.444,-2.462 -20.444,-2.462 -10,0 -18.36,0.35 -20.45,2.462 C -41.62,0.557 -41.78,0.281 -41.84,0 l -0.02,0 0,-0.152 c 0,-0.018 0,-0.036 0,-0.054 0,-0.041 0.01,-0.077 0.01,-0.116 l 1.63,-33.358 0.01,0 c 0.14,-2.61 3.14,-6.56 19.29,-6.56 16.154,0 19.15,3.95 19.291,6.56 l 0.012,0 1.63,33.358 c 0.003,0.039 0.015,0.075 0.015,0.116 0,0.018 -0.012,0.036 -0.012,0.054 L 0.028,0 0,0 Z"
<path style='fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none' d='M 0,0 -0.657,-0.041 -0.657,3.221 C -0.657,4.647 -0.458,5.815 -0.223,5.815 L 2.809,5.815 8.002,5.815 11.03,5.815 C 11.27,5.815 11.46,4.647 11.46,3.221 L 11.46,0.012 C 9.658,0.105 7.779,0.164 5.835,0.164 3.82,0.164 1.863,0.105 0,0 M 15.79,-0.326 15.79,7.549 C 15.79,8.971 14.63,10.15 13.19,10.15 L -2.392,10.15 C -3.813,10.15 -4.982,8.971 -4.982,7.549 L -4.982,-0.416 C -12.69,-1.29 -17.96,-3.06 -17.96,-5.11 L -17.96,-9.003 C -17.96,-9.802 -17.15,-10.56 -15.7,-11.25 -11.9,-13.03 -3.686,-14.27 5.835,-14.27 15.36,-14.27 23.58,-13.03 27.38,-11.25 28.82,-10.56 29.64,-9.802 29.64,-9.003 L 29.64,-5.11 C 29.64,-2.988 23.96,-1.161 15.79,-0.326' id='path1268'/> id="path1264"
</g> inkscape:connector-curvature="0" /></g><g
</g> transform="translate(42.16,71.94)"
</g></g> id="g1266"><path
</svg> style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none"
d="m 0,0 -0.657,-0.041 0,3.262 c 0,1.426 0.199,2.594 0.434,2.594 l 3.032,0 5.193,0 3.028,0 c 0.24,0 0.43,-1.168 0.43,-2.594 l 0,-3.209 C 9.658,0.105 7.779,0.164 5.835,0.164 3.82,0.164 1.863,0.105 0,0 m 15.79,-0.326 0,7.875 c 0,1.422 -1.16,2.601 -2.6,2.601 l -15.582,0 c -1.421,0 -2.59,-1.179 -2.59,-2.601 l 0,-7.965 C -12.69,-1.29 -17.96,-3.06 -17.96,-5.11 l 0,-3.893 c 0,-0.799 0.81,-1.557 2.26,-2.247 3.8,-1.78 12.014,-3.02 21.535,-3.02 9.525,0 17.745,1.24 21.545,3.02 1.44,0.69 2.26,1.448 2.26,2.247 l 0,3.893 c 0,2.122 -5.68,3.949 -13.85,4.784"
id="path1268"
inkscape:connector-curvature="0" /></g></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
viewBox="0 0 70 70"
enable-background="new 0 0 100 100"
xml:space="preserve"
id="svg3590"
inkscape:version="0.91 r13725"
sodipodi:docname="untrade.svg"
width="70"
height="70"><metadata
id="metadata3602"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs3600" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1163"
id="namedview3598"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="5.3400704"
inkscape:cx="-12.866321"
inkscape:cy="22.385961"
inkscape:window-x="1920"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3590" /><path
d="m 12.159535,30.485512 0,-8.546 40.651,0 -5.652,5.652 c -0.879,0.879 -0.879,2.304 0,3.182 0.439,0.439 1.015,0.659 1.591,0.659 0.576,0 1.151,-0.22 1.591,-0.659 l 9.494,-9.493 c 0.014,-0.014 0.023,-0.031 0.037,-0.044 0.087,-0.092 0.17,-0.189 0.241,-0.295 0.024,-0.036 0.04,-0.077 0.063,-0.115 0.052,-0.088 0.104,-0.176 0.145,-0.271 0.02,-0.047 0.028,-0.097 0.045,-0.144 0.031,-0.091 0.064,-0.181 0.084,-0.276 0.029,-0.145 0.045,-0.294 0.045,-0.445 0,-0.151 -0.016,-0.3 -0.045,-0.445 -0.02,-0.097 -0.054,-0.187 -0.085,-0.278 -0.016,-0.047 -0.024,-0.096 -0.044,-0.142 -0.041,-0.1 -0.097,-0.192 -0.151,-0.284 -0.02,-0.034 -0.033,-0.07 -0.056,-0.103 -0.082,-0.123 -0.176,-0.237 -0.279,-0.34 l -9.491,-9.4909998 c -0.879,-0.879 -2.303,-0.879 -3.182,0 -0.879,0.878 -0.879,2.3029998 0,3.1819998 l 5.651,5.651 -42.9029996,0 c -1.242,0 -2.25,1.007 -2.25,2.25 l 0,10.796 c 0,1.243 1.008,2.25 2.25,2.25 1.2419996,0 2.2499996,-1.008 2.2499996,-2.251 z m 47.084,6.258 c -1.242,0 -2.25,1.007 -2.25,2.25 l 0,8.546 -40.651,0 5.652,-5.652 c 0.879,-0.879 0.879,-2.304 0,-3.182 -0.879,-0.879 -2.303,-0.878 -3.182,0 l -9.4939996,9.493 c -0.014,0.013 -0.022,0.03 -0.035,0.043 -0.088,0.092 -0.172,0.189 -0.243,0.296 -0.023,0.035 -0.038,0.074 -0.06,0.11 -0.054,0.09 -0.107,0.179 -0.147,0.276 -0.02,0.046 -0.028,0.095 -0.044,0.141 -0.031,0.092 -0.065,0.183 -0.085,0.279 -0.029,0.146 -0.045,0.294 -0.045,0.445 0,0.151 0.016,0.299 0.045,0.445 0.02,0.097 0.054,0.189 0.085,0.281 0.016,0.046 0.025,0.094 0.044,0.139 0.042,0.102 0.098,0.195 0.153,0.289 0.02,0.032 0.033,0.067 0.054,0.098 0.082,0.123 0.175,0.237 0.279,0.341 l 9.4909996,9.491 c 0.439,0.439 1.015,0.659 1.591,0.659 0.576,0 1.151,-0.22 1.591,-0.659 0.879,-0.878 0.879,-2.303 0,-3.182 l -5.651,-5.651 42.902,0 c 1.242,0 2.25,-1.007 2.25,-2.25 l 0,-10.796 c 0,-1.243 -1.008,-2.25 -2.25,-2.25 z"
id="path3592"
inkscape:connector-curvature="0" /><circle
style="fill:none;stroke:#000000;stroke-width:3.17759514;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4159"
cx="35"
cy="35"
r="33.411201" /><path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:3.00030446;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 13.380987,12.086444 44.81032,48.282085"
id="path4171"
inkscape:connector-curvature="0" /></svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -95,19 +95,8 @@ and_xport_send( const XP_U8* buf, XP_U16 len, const XP_UCHAR* msgNo,
} }
static void 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 static void

View file

@ -1673,8 +1673,6 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1receiveMessage
{ {
jboolean result; jboolean result;
XWJNI_START_GLOBALS(); XWJNI_START_GLOBALS();
XP_ASSERT( state->game.comms );
XP_ASSERT( state->game.server );
XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, globals->vtMgr, XWStreamCtxt* stream = streamFromJStream( MPPARM(mpool) env, globals->vtMgr,
jstream ); jstream );
@ -1686,31 +1684,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_game_1receiveMessage
addrp = &addr; addrp = &addr;
} }
/* pthread_mutex_lock( &state->msgMutex ); */ result = game_receiveMessage( &state->game, stream, addrp );
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 );
}
}
stream_destroy( stream ); stream_destroy( stream );

View file

@ -2128,13 +2128,13 @@ board_requestHint( BoardCtxt* board,
result = nTiles > 0; result = nTiles > 0;
} }
XP_Bool canMove = XP_FALSE;
if ( result ) { if ( result ) {
#ifdef XWFEATURE_SEARCHLIMIT #ifdef XWFEATURE_SEARCHLIMIT
BdHintLimits limits; BdHintLimits limits;
BdHintLimits* lp = NULL; BdHintLimits* lp = NULL;
#endif #endif
XP_Bool wasVisible; XP_Bool wasVisible;
XP_Bool canMove;
wasVisible = setArrowVisible( board, XP_FALSE ); wasVisible = setArrowVisible( board, XP_FALSE );
@ -2194,11 +2194,9 @@ board_requestHint( BoardCtxt* board,
} }
setArrowVisible( board, wasVisible ); setArrowVisible( board, wasVisible );
} }
} else {
util_userError( board->util, ERR_NO_HINT_FOUND );
} }
if ( !result ) { if ( !canMove ) {
util_userError( board->util, ERR_NO_HINT_FOUND ); util_userError( board->util, ERR_NO_HINT_FOUND );
} }
} }

View file

@ -528,7 +528,7 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
newMove->nTiles = 0; newMove->nTiles = 0;
canMove = XP_FALSE; canMove = XP_FALSE;
} }
result = XP_TRUE; XP_ASSERT( result );
} }
util_engineStopping( engine->util ); util_engineStopping( engine->util );

View file

@ -265,6 +265,8 @@ REQUIRED_DEBS = gcc libgtk-3-dev \
libncursesw5-dev \ libncursesw5-dev \
uuid-dev \ uuid-dev \
libsqlite3-dev \ libsqlite3-dev \
libcurl4-openssl-dev \
libjson-c-dev \
.PHONY: debcheck debs_install .PHONY: debcheck debs_install