diff --git a/xwords4/android/app/src/main/AndroidManifest.xml b/xwords4/android/app/src/main/AndroidManifest.xml index 810e8b25a..ccc7396e2 100644 --- a/xwords4/android/app/src/main/AndroidManifest.xml +++ b/xwords4/android/app/src/main/AndroidManifest.xml @@ -179,15 +179,16 @@ - - + + 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 61b033bd3..8b1f3a9b1 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 @@ -21,6 +21,8 @@ package org.eehouse.android.xw4; import android.app.Activity; +import android.app.Notification; +import android.app.PendingIntent; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass.Device.Major; @@ -29,7 +31,8 @@ import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.content.Context; import android.content.Intent; -import android.support.v4.app.JobIntentService; +import android.os.Build; +import android.support.v4.app.NotificationCompat; import junit.framework.Assert; @@ -66,6 +69,8 @@ public class BTService extends XWService { private static final int BT_PROTO = BT_PROTO_JSONS; // change in a release or two private enum BTAction { _NONE, + START_FOREGROUND, + START_BACKGROUND, SCAN, INVITE, SEND, @@ -93,6 +98,8 @@ public class BTService extends XWService { private static final String BT_NAME_KEY = "BT_NAME"; private static final String BT_ADDRESS_KEY = "BT_ADDRESS"; + private static Boolean sInForeground; + private enum BTCmd { BAD_PROTO, PING, @@ -225,12 +232,33 @@ public class BTService extends XWService { return result; } + private static void onAppStateChange( Context context, boolean inForeground ) + { + if ( sInForeground == null || sInForeground != inForeground ) { + sInForeground = inForeground; + + Intent intent = + getIntentTo( context, + inForeground ? BTAction.START_FOREGROUND + : BTAction.START_BACKGROUND ); + startService( context, intent ); + } + } + + static void onAppToForeground( Context context ) + { + onAppStateChange( context, true ); + } + + static void onAppToBackground( Context context ) + { + onAppStateChange( context, false ); + } + public static void startService( Context context ) { if ( XWApp.BTSUPPORTED ) { startService( context, new Intent( context, BTService.class ) ); - // didn't help - // startService( context, new Intent( /*context, BTService.class*/ ) ); } } @@ -322,17 +350,26 @@ public class BTService extends XWService { { Log.d( TAG, "startService(%s)", intent ); - if ( false ) { - // requires asking for Manifest.permission.FOREGROUND_SERVICE + if ( ! sInForeground && canRunForegroundService() ) { context.startForegroundService( intent ); - } else { - JobIntentService.enqueueWork( context, BTService.class, 1111, intent ); + } else if ( sInForeground || Build.VERSION.SDK_INT < Build.VERSION_CODES.O ) { + context.startService( intent ); } } + // We can run a foreground service IIF the OS version is recent enough AND + // user hasn't said not to do it. + private static boolean canRunForegroundService() + { + // added in API level 26 + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + // && Prefs.runForegroundServiceEnabled( context, true ) + ; + } + 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; } @@ -358,7 +395,13 @@ public class BTService extends XWService { @Override public int onStartCommand( Intent intent, int flags, int startId ) { - return handleCommand( intent ); + int result = handleCommand( intent ); + + if ( Service.START_STICKY == result && !sInForeground ) { + startForeground(); + } + + return result; } private int handleCommand( Intent intent ) @@ -375,6 +418,12 @@ public class BTService extends XWService { BTAction cmd = BTAction.values()[ordinal]; Log.i( TAG, "onStartCommand; cmd=%s", cmd.toString() ); switch( cmd ) { + case START_FOREGROUND: + stopForeground( true ); // Kill the notification + // FALLTHRU + case START_BACKGROUND: + break; + case CLEAR: String[] btAddrs = intent.getStringArrayExtra( CLEAR_KEY ); clearDevs( btAddrs ); @@ -450,11 +499,21 @@ public class BTService extends XWService { return result; } // handleCommand() - @Override - protected void onHandleWork( Intent intent ) + private void startForeground() { - Log.e( TAG, "onHandleWork(%s)", intent ); - /*(void)*/handleCommand( intent ); + Intent notifIntent = GamesListDelegate.makeBackgroundIntent( this ); + PendingIntent pendIntent = PendingIntent + .getActivity(this, Utils.nextRandomInt(), notifIntent, PendingIntent.FLAG_ONE_SHOT); + Notification notification = + new NotificationCompat.Builder(this, Utils.CHANNEL_ID) + .setSmallIcon( R.drawable.notify ) + .setContentTitle( BTService.class.getSimpleName() ) + .setContentText("listening for bluetooth messages...") + .setContentIntent(pendIntent) + .build(); + + Log.d( TAG, "calling startForeground()" ); + startForeground( 1337, notification ); } private class BTListenerThread extends Thread { diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java index 086c6cff3..16077063c 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GamesListDelegate.java @@ -2665,6 +2665,12 @@ public class GamesListDelegate extends ListDelegateBase return intent; } + public static Intent makeBackgroundIntent( Context context ) + { + Intent intent = makeSelfIntent( context ); + return intent; + } + public static Intent makeRowidIntent( Context context, long rowid ) { Intent intent = makeSelfIntent( context ); 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 8082adaef..b89c623eb 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,7 +25,6 @@ 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; @@ -201,10 +200,9 @@ public class RelayService extends XWService 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 ); + context.startService( intent ); } } @@ -391,13 +389,6 @@ 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 ) { 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 96f64f19b..8bcfbf672 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 @@ -84,7 +84,7 @@ public class Utils { private static final String TAG = Utils.class.getSimpleName(); public static final int TURN_COLOR = 0x7F00FF00; - private static final String CHANNEL_ID = BuildConfig.APPLICATION_ID + "_channel_id"; + static final String CHANNEL_ID = BuildConfig.APPLICATION_ID + "_channel_id"; private static final String DB_PATH = "XW_GAMES"; private static final String HIDDEN_PREFS = "xwprefs_hidden"; diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java index 307b1230f..7927c4f65 100644 --- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java +++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/XWApp.java @@ -94,16 +94,23 @@ public class XWApp extends Application implements LifecycleObserver { } UpdateCheckReceiver.restartTimer( this ); - BTService.startService( this ); RelayService.startService( this ); GCMIntentService.init( this ); WiDirWrapper.init( this ); } @OnLifecycleEvent(ON_ANY) - public void onAny(LifecycleOwner source, Lifecycle.Event event) + public void onAny( LifecycleOwner source, Lifecycle.Event event ) { - Log.d( TAG, "onAny(%s, %s)", source, event ); + Log.d( TAG, "onAny(%s)", event ); + switch( event ) { + case ON_RESUME: + BTService.onAppToForeground( this ); + break; + case ON_STOP: + BTService.onAppToBackground( this ); + break; + } } // This is called on emulator only, but good for ensuring no memory leaks 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 bd045895a..62e680abf 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,9 +23,7 @@ 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; @@ -39,7 +37,7 @@ import org.eehouse.android.xw4.jni.UtilCtxtImpl; import java.util.HashSet; import java.util.Set; -abstract class XWService extends JobIntentService { +abstract class XWService extends Service { private static final String TAG = XWService.class.getSimpleName(); public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED }; @@ -48,21 +46,10 @@ abstract class XWService extends JobIntentService { 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 - protected void onHandleWork(Intent intent) + public IBinder onBind( Intent intent ) { - Log.e( TAG, "%s.onHandleWork(%s); dropping!!!", getClass().getSimpleName(), - intent ); + return null; } public final static void setListener( MultiService.MultiEventListener li )