Merge branch 'android_branch' into android_translate
|
@ -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.
|
||||
|
||||
IF (and it's a big if) you have all the necessary tools installed (the
|
||||
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.
|
||||
To build and install the debug version of CrossWords:
|
||||
|
||||
The build process requires a file called local.properties that must be
|
||||
generated locally. Generate it before your first build by running
|
||||
# ./gradlew clean insXw4Deb
|
||||
|
||||
# ../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
|
||||
|
||||
Or, if you have a device or emulator attached (listed by 'adb
|
||||
devices'), try
|
||||
|
||||
# ant debug install
|
||||
|
||||
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
|
||||
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
|
||||
homebrew there's only one problem I'm aware of: the parameter 'white'
|
||||
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
|
||||
this change the subset of actionbar icons that are generated from .svg
|
||||
files will be black-on-black.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
def INITIAL_CLIENT_VERS = 8
|
||||
def VERSION_CODE_BASE = 127
|
||||
def VERSION_NAME = '4.4.131'
|
||||
def VERSION_CODE_BASE = 128
|
||||
def VERSION_NAME = '4.4.132'
|
||||
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
|
||||
def GCM_SENDER_ID = System.getenv("GCM_SENDER_ID")
|
||||
def BUILD_INFO_NAME = "build-info.txt"
|
||||
|
@ -201,6 +201,8 @@ dependencies {
|
|||
|
||||
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') {
|
||||
transitive = true;
|
||||
}
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
</style>
|
||||
</head>
|
||||
<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">
|
||||
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
|
||||
|
@ -25,10 +25,14 @@
|
|||
|
||||
<h3>New with this release</h3>
|
||||
<ul>
|
||||
<li>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.</li>
|
||||
<li>Communicate with relay via http when the custom protocol
|
||||
isn't working (e.g. when a wifi router or firewall blocks
|
||||
it.)</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>
|
||||
|
||||
<p>(The full changelog
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -482,14 +482,12 @@ public class GameUtils {
|
|||
if ( force ) {
|
||||
HashMap<Long,CommsConnTypeSet> 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<Long,CommsConnTypeSet> m_games;
|
||||
private ResendDoneProc m_doneProc;
|
||||
private CommsConnType m_filter;
|
||||
private MultiMsgSink m_sink;
|
||||
private int m_nSent = 0;
|
||||
|
||||
public ResendTask( Context context, HashMap<Long,CommsConnTypeSet> 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,10 +119,6 @@ public class MultiMsgSink implements TransportProcs {
|
|||
return nSent;
|
||||
}
|
||||
|
||||
public void relayStatus( CommsRelayState newState )
|
||||
{
|
||||
}
|
||||
|
||||
public void relayErrorProc( XWRELAY_ERROR relayErr )
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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<Integer, PacketData> s_packetsSentUDP = new HashMap<>();
|
||||
private static Map<Integer, PacketData> s_packetsSentWeb = new HashMap<>();
|
||||
private static List<PacketData> s_packetsSentUDP = new ArrayList<>();
|
||||
private static List<PacketData> 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<PacketData> m_queue =
|
||||
new LinkedBlockingQueue<PacketData>();
|
||||
private Thread m_fetchThread = null; // no longer used
|
||||
private AtomicReference<UDPThreads> 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<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 );
|
||||
}
|
||||
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<PacketData> list = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
|
||||
synchronized( list ) {
|
||||
list.add(packet );
|
||||
}
|
||||
}
|
||||
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 ) {
|
||||
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<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 );
|
||||
}
|
||||
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<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 Context m_context;
|
||||
private HashMap<String,ArrayList<byte[]>> 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<Integer, PacketData> map = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
|
||||
Assert.assertTrue( packetID != 0 );
|
||||
List<PacketData> map = fromUDP ? s_packetsSentUDP : s_packetsSentWeb;
|
||||
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 ) {
|
||||
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<String> 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 {}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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<QueueElem> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
<item android:id="@+id/board_menu_trade_cancel"
|
||||
android:title="@string/button_trade_cancel"
|
||||
android:showAsAction="ifRoom"
|
||||
android:icon="@drawable/back__gen"
|
||||
android:icon="@drawable/untrade__gen"
|
||||
/>
|
||||
|
||||
<item android:id="@+id/board_menu_trade_commit"
|
||||
|
|
|
@ -126,6 +126,8 @@
|
|||
<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_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_download_path">key_download_path</string>
|
||||
<string name="key_got_langdict">key_got_langdict</string>
|
||||
|
|
|
@ -1692,7 +1692,7 @@
|
|||
<string name="about_vers_fmt">CrossWords for Android, Version %1$s,
|
||||
rev %2$s, built on %3$s.</string>
|
||||
<!-- 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
|
||||
License.</string>
|
||||
|
||||
|
@ -2601,6 +2601,12 @@
|
|||
<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="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 -->
|
||||
<plurals name="resent_msgs_fmt">
|
||||
<item quantity="one">One move sent</item>
|
||||
|
|
|
@ -390,28 +390,6 @@
|
|||
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"
|
||||
android:summary="@string/pref_group_relay_summary"
|
||||
>
|
||||
|
@ -420,6 +398,15 @@
|
|||
android:summary="@string/enable_relay_toself_summary"
|
||||
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
|
||||
android:key="@string/key_relay_host"
|
||||
android:title="@string/relay_host"
|
||||
|
@ -452,6 +439,28 @@
|
|||
/>
|
||||
</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"
|
||||
android:summary="@string/pref_group_l10n_summary"
|
||||
>
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
1
xwords4/android/app/src/xw4d/res/values/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
strings.xml
|
|
@ -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">
|
||||
<metadata
|
||||
id="metadata3416">
|
||||
<rdf:RDF>
|
||||
|
@ -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">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3510" />
|
||||
id="grid3510"
|
||||
originx="-17"
|
||||
originy="-14" />
|
||||
</sodipodi:namedview>
|
||||
<title
|
||||
id="title3398">01</title>
|
||||
<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"
|
||||
inkscape:connector-curvature="0" />
|
||||
<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"
|
||||
transform="translate(0,-5)" />
|
||||
transform="translate(-17,-9)" />
|
||||
<rect
|
||||
x="25"
|
||||
y="15"
|
||||
x="8"
|
||||
y="11"
|
||||
width="50"
|
||||
height="6"
|
||||
id="rect3404" />
|
||||
<rect
|
||||
x="30"
|
||||
y="4"
|
||||
x="13"
|
||||
y="0"
|
||||
width="40"
|
||||
height="6"
|
||||
id="rect3406" />
|
||||
|
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.2 KiB |
|
@ -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 |
|
@ -1,9 +1,49 @@
|
|||
<?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
|
||||
<?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"
|
||||
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"
|
||||
transform="matrix(1.25,0,0,-1.25,0,120)">
|
||||
<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>
|
||||
</svg>
|
||||
transform="matrix(1.25,0,0,-1.25,-22.4875,105.0125)"><path
|
||||
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||
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 |
|
@ -1,18 +1,59 @@
|
|||
<?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
|
||||
<?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"
|
||||
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"
|
||||
transform="matrix(1.25,0,0,-1.25,0,120)">
|
||||
<g id='g1254'>
|
||||
<g id='g1256'>
|
||||
<g transform='translate(68.92,54.16)' id='g1262'>
|
||||
<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'/>
|
||||
</g>
|
||||
<g transform='translate(42.16,71.94)' id='g1266'>
|
||||
<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'/>
|
||||
</g>
|
||||
</g>
|
||||
</g></g>
|
||||
</svg>
|
||||
transform="matrix(1.25,0,0,-1.25,-30.25,102.6125)"><g
|
||||
id="g1254"><g
|
||||
id="g1256"><g
|
||||
transform="translate(68.92,54.16)"
|
||||
id="g1262"><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.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"
|
||||
id="path1264"
|
||||
inkscape:connector-curvature="0" /></g><g
|
||||
transform="translate(42.16,71.94)"
|
||||
id="g1266"><path
|
||||
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 |
59
xwords4/android/img_src/untrade.svg
Normal 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 |
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|