use foregroundService on devices that can handle it

And show the notification regardless. Tested only on pre-Oreo device so far.
This commit is contained in:
Eric House 2018-11-22 15:18:57 -08:00
parent 6177914213
commit 2ba5982ddf
7 changed files with 99 additions and 48 deletions

View file

@ -179,15 +179,16 @@
<activity android:name=".loc.LocItemEditActivity" <activity android:name=".loc.LocItemEditActivity"
/> />
<service android:name="RelayService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"
/>
<service android:name="BTService" <service android:name="BTService"
android:permission="android.permission.BIND_JOB_SERVICE" android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" android:exported="false"
/> />
<service android:name="SMSService"/> <service android:name="SMSService"
android:exported="false"
/>
<service android:name="RelayService"
android:exported="false"
/>
<receiver android:name=".MountEventReceiver"> <receiver android:name=".MountEventReceiver">
<intent-filter> <intent-filter>

View file

@ -21,6 +21,8 @@
package org.eehouse.android.xw4; package org.eehouse.android.xw4;
import android.app.Activity; import android.app.Activity;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass.Device.Major; import android.bluetooth.BluetoothClass.Device.Major;
@ -29,7 +31,8 @@ import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket; import android.bluetooth.BluetoothSocket;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.support.v4.app.JobIntentService; import android.os.Build;
import android.support.v4.app.NotificationCompat;
import junit.framework.Assert; 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 static final int BT_PROTO = BT_PROTO_JSONS; // change in a release or two
private enum BTAction { _NONE, private enum BTAction { _NONE,
START_FOREGROUND,
START_BACKGROUND,
SCAN, SCAN,
INVITE, INVITE,
SEND, SEND,
@ -93,6 +98,8 @@ public class BTService extends XWService {
private static final String BT_NAME_KEY = "BT_NAME"; private static final String BT_NAME_KEY = "BT_NAME";
private static final String BT_ADDRESS_KEY = "BT_ADDRESS"; private static final String BT_ADDRESS_KEY = "BT_ADDRESS";
private static Boolean sInForeground;
private enum BTCmd { private enum BTCmd {
BAD_PROTO, BAD_PROTO,
PING, PING,
@ -225,12 +232,33 @@ public class BTService extends XWService {
return result; 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 ) public static void startService( Context context )
{ {
if ( XWApp.BTSUPPORTED ) { if ( XWApp.BTSUPPORTED ) {
startService( context, new Intent( context, BTService.class ) ); 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 ); Log.d( TAG, "startService(%s)", intent );
if ( false ) { if ( ! sInForeground && canRunForegroundService() ) {
// requires asking for Manifest.permission.FOREGROUND_SERVICE
context.startForegroundService( intent ); context.startForegroundService( intent );
} else { } else if ( sInForeground || Build.VERSION.SDK_INT < Build.VERSION_CODES.O ) {
JobIntentService.enqueueWork( context, BTService.class, 1111, intent ); 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 ) 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() ); intent.putExtra( CMD_KEY, cmd.ordinal() );
return intent; return intent;
} }
@ -358,7 +395,13 @@ public class BTService extends XWService {
@Override @Override
public int onStartCommand( Intent intent, int flags, int startId ) 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 ) private int handleCommand( Intent intent )
@ -375,6 +418,12 @@ public class BTService extends XWService {
BTAction cmd = BTAction.values()[ordinal]; BTAction cmd = BTAction.values()[ordinal];
Log.i( TAG, "onStartCommand; cmd=%s", cmd.toString() ); Log.i( TAG, "onStartCommand; cmd=%s", cmd.toString() );
switch( cmd ) { switch( cmd ) {
case START_FOREGROUND:
stopForeground( true ); // Kill the notification
// FALLTHRU
case START_BACKGROUND:
break;
case CLEAR: case CLEAR:
String[] btAddrs = intent.getStringArrayExtra( CLEAR_KEY ); String[] btAddrs = intent.getStringArrayExtra( CLEAR_KEY );
clearDevs( btAddrs ); clearDevs( btAddrs );
@ -450,11 +499,21 @@ public class BTService extends XWService {
return result; return result;
} // handleCommand() } // handleCommand()
@Override private void startForeground()
protected void onHandleWork( Intent intent )
{ {
Log.e( TAG, "onHandleWork(%s)", intent ); Intent notifIntent = GamesListDelegate.makeBackgroundIntent( this );
/*(void)*/handleCommand( intent ); 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 { private class BTListenerThread extends Thread {

View file

@ -2665,6 +2665,12 @@ public class GamesListDelegate extends ListDelegateBase
return intent; return intent;
} }
public static Intent makeBackgroundIntent( Context context )
{
Intent intent = makeSelfIntent( context );
return intent;
}
public static Intent makeRowidIntent( Context context, long rowid ) public static Intent makeRowidIntent( Context context, long rowid )
{ {
Intent intent = makeSelfIntent( context ); Intent intent = makeSelfIntent( context );

View file

@ -25,7 +25,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.support.v4.app.JobIntentService;
import android.text.TextUtils; import android.text.TextUtils;
import junit.framework.Assert; import junit.framework.Assert;
@ -201,10 +200,9 @@ public class RelayService extends XWService
Log.d( TAG, "startService(%s)", intent ); Log.d( TAG, "startService(%s)", intent );
if ( false ) { if ( false ) {
// requires asking for Manifest.permission.FOREGROUND_SERVICE
context.startForegroundService( intent ); context.startForegroundService( intent );
} else { } else {
JobIntentService.enqueueWork( context, RelayService.class, 1112, intent ); context.startService( intent );
} }
} }
@ -391,13 +389,6 @@ public class RelayService extends XWService
super.onDestroy(); super.onDestroy();
} }
@Override
protected void onHandleWork( Intent intent )
{
Log.e( TAG, "onHandleWork(%s)", intent );
handleCommand( intent );
}
// NetStateCache.StateChangedIf interface // NetStateCache.StateChangedIf interface
public void onNetAvail( boolean nowAvailable ) public void onNetAvail( boolean nowAvailable )
{ {

View file

@ -84,7 +84,7 @@ public class Utils {
private static final String TAG = Utils.class.getSimpleName(); private static final String TAG = Utils.class.getSimpleName();
public static final int TURN_COLOR = 0x7F00FF00; 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 DB_PATH = "XW_GAMES";
private static final String HIDDEN_PREFS = "xwprefs_hidden"; private static final String HIDDEN_PREFS = "xwprefs_hidden";

View file

@ -94,16 +94,23 @@ public class XWApp extends Application implements LifecycleObserver {
} }
UpdateCheckReceiver.restartTimer( this ); UpdateCheckReceiver.restartTimer( this );
BTService.startService( this );
RelayService.startService( this ); RelayService.startService( this );
GCMIntentService.init( this ); GCMIntentService.init( this );
WiDirWrapper.init( this ); WiDirWrapper.init( this );
} }
@OnLifecycleEvent(ON_ANY) @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 // This is called on emulator only, but good for ensuring no memory leaks

View file

@ -23,9 +23,7 @@ package org.eehouse.android.xw4;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.os.IBinder; import android.os.IBinder;
import android.support.v4.app.JobIntentService;
import junit.framework.Assert; import junit.framework.Assert;
@ -39,7 +37,7 @@ import org.eehouse.android.xw4.jni.UtilCtxtImpl;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
abstract class XWService extends JobIntentService { abstract class XWService extends Service {
private static final String TAG = XWService.class.getSimpleName(); private static final String TAG = XWService.class.getSimpleName();
public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED }; public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED };
@ -48,21 +46,10 @@ abstract class XWService extends JobIntentService {
private UtilCtxt m_utilCtxt; 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 @Override
protected void onHandleWork(Intent intent) public IBinder onBind( Intent intent )
{ {
Log.e( TAG, "%s.onHandleWork(%s); dropping!!!", getClass().getSimpleName(), return null;
intent );
} }
public final static void setListener( MultiService.MultiEventListener li ) public final static void setListener( MultiService.MultiEventListener li )