diff --git a/xwords4/android/app/src/main/AndroidManifest.xml b/xwords4/android/app/src/main/AndroidManifest.xml
index 27df1905e..810e8b25a 100644
--- a/xwords4/android/app/src/main/AndroidManifest.xml
+++ b/xwords4/android/app/src/main/AndroidManifest.xml
@@ -179,8 +179,14 @@
-
-
+
+
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTService.java
index 98d851b16..61b033bd3 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTService.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BTService.java
@@ -29,6 +29,7 @@ import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
+import android.support.v4.app.JobIntentService;
import junit.framework.Assert;
@@ -228,6 +229,8 @@ public class BTService extends XWService {
{
if ( XWApp.BTSUPPORTED ) {
startService( context, new Intent( context, BTService.class ) );
+ // didn't help
+ // startService( context, new Intent( /*context, BTService.class*/ ) );
}
}
@@ -317,12 +320,19 @@ public class BTService extends XWService {
private static void startService( Context context, Intent intent )
{
- context.startService( intent );
+ Log.d( TAG, "startService(%s)", intent );
+
+ if ( false ) {
+ // requires asking for Manifest.permission.FOREGROUND_SERVICE
+ context.startForegroundService( intent );
+ } else {
+ JobIntentService.enqueueWork( context, BTService.class, 1111, intent );
+ }
}
private static Intent getIntentTo( Context context, BTAction cmd )
{
- Intent intent = new Intent( context, BTService.class );
+ Intent intent = new Intent( /*context, BTService.class*/ );
intent.putExtra( CMD_KEY, cmd.ordinal() );
return intent;
}
@@ -347,6 +357,11 @@ public class BTService extends XWService {
@Override
public int onStartCommand( Intent intent, int flags, int startId )
+ {
+ return handleCommand( intent );
+ }
+
+ private int handleCommand( Intent intent )
{
int result;
if ( XWApp.BTSUPPORTED && null != intent ) {
@@ -433,7 +448,14 @@ public class BTService extends XWService {
result = Service.START_STICKY_COMPATIBILITY;
}
return result;
- } // onStartCommand()
+ } // handleCommand()
+
+ @Override
+ protected void onHandleWork( Intent intent )
+ {
+ Log.e( TAG, "onHandleWork(%s)", intent );
+ /*(void)*/handleCommand( intent );
+ }
private class BTListenerThread extends Thread {
private BluetoothServerSocket m_serverSocket;
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardView.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardView.java
index d0c9f33de..fff413910 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardView.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardView.java
@@ -46,7 +46,6 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
private static final float MIN_FONT_DIPS = 10.0f;
private static final int MULTI_INACTIVE = -1;
- private static final int VERSION_CODES_N = 24; // until we're building on SDK 24...
private static boolean s_isFirstDraw;
private static int s_curGameID;
@@ -205,7 +204,7 @@ public class BoardView extends View implements BoardHandler, SyncedDraw {
synchronized( this ) {
if ( layoutBoardOnce() && m_measuredFromDims ) {
Bitmap bitmap = s_bitmap;
- if ( Build.VERSION.SDK_INT >= VERSION_CODES_N ) {
+ if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ) {
bitmap = Bitmap.createBitmap(bitmap);
}
canvas.drawBitmap( bitmap, 0, 0, new Paint() );
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DbgUtils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DbgUtils.java
index f6116d8de..db5664334 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DbgUtils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/DbgUtils.java
@@ -25,7 +25,6 @@ import android.content.Intent;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.os.Bundle;
-import android.os.Looper;
import android.text.TextUtils;
import android.text.format.Time;
@@ -103,7 +102,7 @@ public class DbgUtils {
public static void assertOnUIThread()
{
- Assert.assertTrue( Looper.getMainLooper().equals(Looper.myLooper()) );
+ Assert.assertTrue( Utils.isOnUIThread() );
}
public static void printStack( String tag, StackTraceElement[] trace )
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetStateCache.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetStateCache.java
index 3081ccc41..101f52059 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetStateCache.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/NetStateCache.java
@@ -54,17 +54,21 @@ public class NetStateCache {
public static void register( Context context, StateChangedIf proc )
{
- initIfNot( context );
- synchronized( s_ifs ) {
- s_ifs.add( proc );
+ if ( Utils.isOnUIThread() ) {
+ initIfNot( context );
+ synchronized( s_ifs ) {
+ s_ifs.add( proc );
+ }
}
}
public static void unregister( Context context, StateChangedIf proc )
{
- initIfNot( context );
- synchronized( s_ifs ) {
- s_ifs.remove( proc );
+ if ( Utils.isOnUIThread() ) {
+ initIfNot( context );
+ synchronized( s_ifs ) {
+ s_ifs.remove( proc );
+ }
}
}
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
index 0b79002e5..8082adaef 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/RelayService.java
@@ -25,6 +25,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
+import android.support.v4.app.JobIntentService;
import android.text.TextUtils;
import junit.framework.Assert;
@@ -192,13 +193,25 @@ public class RelayService extends XWService
{
Log.i( TAG, "startService()" );
Intent intent = getIntentTo( context, MsgCmds.UDP_CHANGED );
- context.startService( intent );
+ startService( context, intent );
+ }
+
+ private static void startService( Context context, Intent intent )
+ {
+ Log.d( TAG, "startService(%s)", intent );
+
+ if ( false ) {
+ // requires asking for Manifest.permission.FOREGROUND_SERVICE
+ context.startForegroundService( intent );
+ } else {
+ JobIntentService.enqueueWork( context, RelayService.class, 1112, intent );
+ }
}
private static void stopService( Context context )
{
Intent intent = getIntentTo( context, MsgCmds.STOP );
- context.startService( intent );
+ startService( context, intent );
}
public static void inviteRemote( Context context, int destDevID,
@@ -206,7 +219,7 @@ public class RelayService extends XWService
{
int myDevID = DevID.getRelayDevIDInt( context );
if ( 0 != myDevID ) {
- context.startService( getIntentTo( context, MsgCmds.INVITE )
+ startService( context, getIntentTo( context, MsgCmds.INVITE )
.putExtra( DEV_ID_SRC, myDevID )
.putExtra( DEV_ID_DEST, destDevID )
.putExtra( RELAY_ID, relayID )
@@ -217,13 +230,13 @@ public class RelayService extends XWService
public static void reset( Context context )
{
Intent intent = getIntentTo( context, MsgCmds.RESET );
- context.startService( intent );
+ startService( context, intent );
}
public static void timerFired( Context context )
{
Intent intent = getIntentTo( context, MsgCmds.TIMER_FIRED );
- context.startService( intent );
+ startService( context, intent );
}
public static int sendPacket( Context context, long rowid, byte[] msg )
@@ -234,7 +247,7 @@ public class RelayService extends XWService
Intent intent = getIntentTo( context, MsgCmds.SEND )
.putExtra( ROWID, rowid )
.putExtra( BINBUFFER, msg );
- context.startService( intent );
+ startService( context, intent );
result = msg.length;
} else {
Log.w( TAG, "sendPacket: network down" );
@@ -251,7 +264,7 @@ public class RelayService extends XWService
.putExtra( ROWID, rowid )
.putExtra( RELAY_ID, relayID )
.putExtra( BINBUFFER, msg );
- context.startService( intent );
+ startService( context, intent );
result = msg.length;
}
return result;
@@ -287,7 +300,7 @@ public class RelayService extends XWService
Intent intent = getIntentTo( context, MsgCmds.RECEIVE )
.putExtra( ROWID, rowid )
.putExtra( BINBUFFER, msg );
- context.startService( intent );
+ startService( context, intent );
} else {
Log.w( TAG, "postData(): Dropping message for rowid %d:"
+ " not on device", rowid );
@@ -305,14 +318,14 @@ public class RelayService extends XWService
Intent intent = getIntentTo( context, MsgCmds.PROCESS_GAME_MSGS )
.putExtra( MSGS_ARR, msgs64 )
.putExtra( RELAY_ID, relayId );
- context.startService( intent );
+ startService( context, intent );
}
public static void processDevMsgs( Context context, String[] msgs64 )
{
Intent intent = getIntentTo( context, MsgCmds.PROCESS_DEV_MSGS )
.putExtra( MSGS_ARR, msgs64 );
- context.startService( intent );
+ startService( context, intent );
}
private static Intent getIntentTo( Context context, MsgCmds cmd )
@@ -356,94 +369,7 @@ public class RelayService extends XWService
@Override
public int onStartCommand( Intent intent, int flags, int startId )
{
- Integer result = null;
- if ( null != intent ) {
- MsgCmds cmd;
- try {
- cmd = MsgCmds.values()[intent.getIntExtra( CMD_STR, -1 )];
- } catch (Exception ex) { // OOB most likely
- cmd = null;
- }
- if ( null != cmd ) {
- // Log.d( TAG, "onStartCommand(): cmd=%s", cmd.toString() );
- switch( cmd ) {
- case PROCESS_GAME_MSGS:
- String[] relayIDs = new String[1];
- relayIDs[0] = intent.getStringExtra( RELAY_ID );
- long[] rowIDs = DBUtils.getRowIDsFor( this, relayIDs[0] );
- if ( 0 < rowIDs.length ) {
- byte[][][] msgs = expandMsgsArray( intent );
- process( msgs, rowIDs, relayIDs );
- }
- break;
- case PROCESS_DEV_MSGS:
- byte[][][] msgss = expandMsgsArray( intent );
- for ( byte[][] msgs : msgss ) {
- for ( byte[] msg : msgs ) {
- gotPacket( msg, true, false );
- }
- }
- break;
- case UDP_CHANGED:
- startThreads();
- break;
- case RESET:
- stopThreads();
- startThreads();
- break;
- case UPGRADE:
- UpdateCheckReceiver.checkVersions( this, false );
- break;
- case GOT_INVITE:
- int srcDevID = intent.getIntExtra( INVITE_FROM, 0 );
- NetLaunchInfo nli
- = NetLaunchInfo.makeFrom( this, intent.getStringExtra(NLI_DATA) );
- receiveInvitation( srcDevID, nli );
- break;
- case SEND:
- case RECEIVE:
- case SENDNOCONN:
- startUDPThreadsIfNot();
- long rowid = intent.getLongExtra( ROWID, -1 );
- byte[] msg = intent.getByteArrayExtra( BINBUFFER );
- if ( MsgCmds.SEND == cmd ) {
- sendMessage( rowid, msg );
- } else if ( MsgCmds.SENDNOCONN == cmd ) {
- String relayID = intent.getStringExtra( RELAY_ID );
- sendNoConnMessage( rowid, relayID, msg );
- } else {
- receiveMessage( this, rowid, null, msg, s_addr );
- }
- break;
- case INVITE:
- startUDPThreadsIfNot();
- srcDevID = intent.getIntExtra( DEV_ID_SRC, 0 );
- int destDevID = intent.getIntExtra( DEV_ID_DEST, 0 );
- String relayID = intent.getStringExtra( RELAY_ID );
- String nliData = intent.getStringExtra( NLI_DATA );
- sendInvitation( srcDevID, destDevID, relayID, nliData );
- break;
- case TIMER_FIRED:
- if ( !NetStateCache.netAvail( this ) ) {
- Log.w( TAG, "not connecting: no network" );
- } else if ( startFetchThreadIfNotUDP() ) {
- // do nothing
- } else if ( registerWithRelayIfNot() ) {
- requestMessages();
- }
- RelayReceiver.setTimer( this );
- break;
- case STOP:
- stopThreads();
- stopSelf();
- break;
- default:
- Assert.fail();
- }
-
- result = Service.START_STICKY;
- }
- }
+ Integer result = handleCommand( intent );
if ( null == result ) {
result = Service.START_STICKY_COMPATIBILITY;
@@ -465,12 +391,110 @@ public class RelayService extends XWService
super.onDestroy();
}
+ @Override
+ protected void onHandleWork( Intent intent )
+ {
+ Log.e( TAG, "onHandleWork(%s)", intent );
+ handleCommand( intent );
+ }
+
// NetStateCache.StateChangedIf interface
public void onNetAvail( boolean nowAvailable )
{
startService( this ); // bad name: will *stop* threads too
}
+ private Integer handleCommand( Intent intent )
+ {
+ Integer result = null;
+ MsgCmds cmd;
+ try {
+ cmd = MsgCmds.values()[intent.getIntExtra( CMD_STR, -1 )];
+ } catch (Exception ex) { // OOB most likely
+ cmd = null;
+ }
+ if ( null != cmd ) {
+ // Log.d( TAG, "onStartCommand(): cmd=%s", cmd.toString() );
+ switch( cmd ) {
+ case PROCESS_GAME_MSGS:
+ String[] relayIDs = new String[1];
+ relayIDs[0] = intent.getStringExtra( RELAY_ID );
+ long[] rowIDs = DBUtils.getRowIDsFor( this, relayIDs[0] );
+ if ( 0 < rowIDs.length ) {
+ byte[][][] msgs = expandMsgsArray( intent );
+ process( msgs, rowIDs, relayIDs );
+ }
+ break;
+ case PROCESS_DEV_MSGS:
+ byte[][][] msgss = expandMsgsArray( intent );
+ for ( byte[][] msgs : msgss ) {
+ for ( byte[] msg : msgs ) {
+ gotPacket( msg, true, false );
+ }
+ }
+ break;
+ case UDP_CHANGED:
+ startThreads();
+ break;
+ case RESET:
+ stopThreads();
+ startThreads();
+ break;
+ case UPGRADE:
+ UpdateCheckReceiver.checkVersions( this, false );
+ break;
+ case GOT_INVITE:
+ int srcDevID = intent.getIntExtra( INVITE_FROM, 0 );
+ NetLaunchInfo nli
+ = NetLaunchInfo.makeFrom( this, intent.getStringExtra(NLI_DATA) );
+ receiveInvitation( srcDevID, nli );
+ break;
+ case SEND:
+ case RECEIVE:
+ case SENDNOCONN:
+ startUDPThreadsIfNot();
+ long rowid = intent.getLongExtra( ROWID, -1 );
+ byte[] msg = intent.getByteArrayExtra( BINBUFFER );
+ if ( MsgCmds.SEND == cmd ) {
+ sendMessage( rowid, msg );
+ } else if ( MsgCmds.SENDNOCONN == cmd ) {
+ String relayID = intent.getStringExtra( RELAY_ID );
+ sendNoConnMessage( rowid, relayID, msg );
+ } else {
+ receiveMessage( this, rowid, null, msg, s_addr );
+ }
+ break;
+ case INVITE:
+ startUDPThreadsIfNot();
+ srcDevID = intent.getIntExtra( DEV_ID_SRC, 0 );
+ int destDevID = intent.getIntExtra( DEV_ID_DEST, 0 );
+ String relayID = intent.getStringExtra( RELAY_ID );
+ String nliData = intent.getStringExtra( NLI_DATA );
+ sendInvitation( srcDevID, destDevID, relayID, nliData );
+ break;
+ case TIMER_FIRED:
+ if ( !NetStateCache.netAvail( this ) ) {
+ Log.w( TAG, "not connecting: no network" );
+ } else if ( startFetchThreadIfNotUDP() ) {
+ // do nothing
+ } else if ( registerWithRelayIfNot() ) {
+ requestMessages();
+ }
+ RelayReceiver.setTimer( this );
+ break;
+ case STOP:
+ stopThreads();
+ stopSelf();
+ break;
+ default:
+ Assert.fail();
+ }
+
+ result = Service.START_STICKY;
+ }
+ return result;
+ }
+
private void setupNotifications( String[] relayIDs, BackMoveResult[] bmrs,
ArrayList locals )
{
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
index f83fd30a8..96f64f19b 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/Utils.java
@@ -40,6 +40,7 @@ import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
+import android.os.Looper;
import android.provider.ContactsContract.PhoneLookup;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.FileProvider;
@@ -533,6 +534,11 @@ public class Utils {
return result;
}
+ public static boolean isOnUIThread()
+ {
+ return Looper.getMainLooper().equals(Looper.myLooper());
+ }
+
public static String base64Encode( byte[] in )
{
return Base64.encodeToString( in, Base64.NO_WRAP );
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWService.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWService.java
index 62e680abf..bd045895a 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWService.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWService.java
@@ -23,7 +23,9 @@ package org.eehouse.android.xw4;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.os.IBinder;
+import android.support.v4.app.JobIntentService;
import junit.framework.Assert;
@@ -37,7 +39,7 @@ import org.eehouse.android.xw4.jni.UtilCtxtImpl;
import java.util.HashSet;
import java.util.Set;
-abstract class XWService extends Service {
+abstract class XWService extends JobIntentService {
private static final String TAG = XWService.class.getSimpleName();
public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED };
@@ -46,10 +48,21 @@ abstract class XWService extends Service {
private UtilCtxt m_utilCtxt;
+ // @Override
+ // public IBinder onBind( Intent intent )
+ // {
+ // IBinder result = null;
+ // if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) {
+ // result = super.onBind( intent );
+ // }
+ // return result;
+ // }
+
@Override
- public IBinder onBind( Intent intent )
+ protected void onHandleWork(Intent intent)
{
- return null;
+ Log.e( TAG, "%s.onHandleWork(%s); dropping!!!", getClass().getSimpleName(),
+ intent );
}
public final static void setListener( MultiService.MultiEventListener li )