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 )