Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Oreo - Starting a service in the foreground

I have created a service that tracks the device's location as it moves. The service is started in by an Activity that binds to it, and in this activity there is a "Start Tracking" button. When this button is pressed, I need the service to start in the foreground, so that it stores the Locations that the device has moved to, even if the Activity that binds to it is closed, or the app is minimised.

I understand that for the Service to be in the foreground, a notification must be displayed. I have attempted to do so, but I cannot get the notification to show or the Service to work in the foreground when the activity is destroyed.

It appears that notifications have changed in Oreo due to the notification channels, but I cannot figure out what needs to be done differently. The device I am testing this on is on 8.0.0.

Here is mys service:

public class LocationTrackerService extends Service {

    private LocationListener locationListener;
    private LocationManager locationManager;
    private IBinder binder = new LocalBinder();
    private boolean isTracking;
    private ArrayList<Location> trackedWaypoints;
    private String bestProvider;
    private Timer timer;
    private Distance distance;

    @SuppressLint("MissingPermission")
    @Override
    public void onCreate() {
        super.onCreate();

        locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
        Criteria criteria = new Criteria();
        bestProvider = locationManager.getBestProvider(criteria, true);

        isTracking = false;

        locationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                Intent intent = new Intent("location_update");
                intent.putExtra("latitude", location.getLatitude());
                intent.putExtra("longitude", location.getLongitude());
                sendBroadcast(intent);
                if (isTracking) {
                    if (trackedWaypoints.size() > 1) {
                        distance.add(trackedWaypoints.get(trackedWaypoints.size() - 1).distanceTo(location));
                    }
                    trackedWaypoints.add(location);
                }
            }

            @Override
            public void onStatusChanged(String s, int i, Bundle bundle) { }

            @Override
            public void onProviderEnabled(String s) { }

            @Override
            public void onProviderDisabled(String s) {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
            }
        };

        locationManager.requestLocationUpdates(bestProvider, 0, 0, locationListener);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (locationManager != null) {
            locationManager.removeUpdates(locationListener);
        }
    }

    public void startTracking() {
        trackedWaypoints = new ArrayList<Location>();
        timer = new Timer();
        distance = new Distance();
        timer.start();
        isTracking = true;
        startInForeground();
    }

    private void startInForeground() {
        Intent notificationIntent = new Intent(this, WorkoutActivity.class);
        PendingIntent pendingIntent =
                PendingIntent.getActivity(this, 0, notificationIntent, 0);

        Notification notification =
                new Notification.Builder(this)
                        .setContentTitle("TEST")
                        .setContentText("HELLO")
                        .setSmallIcon(R.drawable.ic_directions_run_black_24dp)
                        .setContentIntent(pendingIntent)
                        .setTicker("TICKER")
                        .build();

        startForeground(101, notification);
    }

    public void stopTracking() {
        isTracking = false;
        stopForeground(true);
    }

    public boolean isTracking() {
        return isTracking;
    }

    public ArrayList<Location> getTrackedWaypoints() {
        return trackedWaypoints;
    }

    public Timer getTime() {
        timer.update();
        return timer;
    }

    public Distance getDistance() {
        return distance;
    }

    public int getSteps() {
        return 0;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    public class LocalBinder extends Binder {
        public LocationTrackerService getLocationTrackerInstance() {
            return LocationTrackerService.this;
        }
    }
}
like image 508
KOB Avatar asked Dec 02 '17 18:12

KOB


People also ask

How do you start the foreground in Android Oreo?

The new context. startForegroundService() method starts a foreground service but with a implicit contract that service will call start foreground within 5 second of its creation. You can also call startService and startForegroundService for different OS version.

How do I start a foreground service?

Inside the service, usually in onStartCommand() , you can request that your service run in the foreground. To do so, call startForeground() . This method takes two parameters: a positive integer that uniquely identifies the notification in the status bar and the Notification object itself.

What does foreground service mean?

A foreground service performs some operation that is noticeable to the user. For example, an audio app would use a foreground service to play an audio track. Foreground services must display a Notification. Foreground services continue running even when the user isn't interacting with the app.


2 Answers

Try to change your startInForeground() method with this code:

private void startInForeground() {
        Intent notificationIntent = new Intent(this, WorkoutActivity.class);
        PendingIntent pendingIntent=PendingIntent.getActivity(this,0,notificationIntent,0);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this,NOTIFICATION_CHANNEL_ID)
                .setSmallIcon(R.drawable.shsl_notification)
                .setContentTitle("TEST")
                .setContentText("HELLO")
                .setTicker("TICKER") 
                .setContentIntent(pendingIntent);
        Notification notification=builder.build();
        if(Build.VERSION.SDK_INT>=26) {
            NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
            channel.setDescription(NOTIFICATION_CHANNEL_DESC);
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.createNotificationChannel(channel);
        }
        startForeground(NOTIFICATION_ID, notification);
}

So, when your Android version is Oreo (or higher) notification channel will be created, otherwise not.

like image 187
colens Avatar answered Sep 28 '22 02:09

colens


Before show notification you have to create notification channel:

private void createNotificationChannel() {
    if (Build.VERSION_CODES.O <= Build.VERSION.SDK_INT) {
        NotificationChannel notificationChannel =
                new NotificationChannel(PRIMARY_CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
        notificationManager.createNotificationChannel(notificationChannel);
    }
}

and then change create notification builder

new Notification.Builder(context, PRIMARY_CHANNEL_ID)
like image 34
mac229 Avatar answered Sep 28 '22 01:09

mac229