No location updates in foreground service Android Q(XIAOMI device) when activity is not visible to user

Viewed 555

I am making the prototype app, and it's only function is to recieve location updates from foreground service. I think I already read everything in Stackoverflow and in documentation about how to get location updates from foreground service. But app still recieve location updates ONLY when it is visible on the screen. App has only one activity with two buttons - start and stop service, and TextView where I show location updates. I have this permissions in manifest:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

this is my LocationService declaration in manifest:

 <service
            android:name=".LocationService"
            android:enabled="true"
            android:exported="true"
            android:foregroundServiceType="location" />

here is my LocationService java class code:

public class LocationService extends Service implements LocationListener {
 private Binder binder;

    public LocationUpdateListener getUpdateListener() {
        return updateListener;
    }

    private LocationUpdateListener updateListener;
    private LocationManager locationManager;
    private String locations = "";

    public final static String NOTIFICATION_CHANEL_ID = "LocationServiceChannelRu";

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

    @Override
    public void onCreate() {
        super.onCreate();
        binder = new Binder();
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        Criteria locationCriteria = new Criteria();
        locationCriteria.setAccuracy(Criteria.NO_REQUIREMENT);
        locationCriteria.setPowerRequirement(Criteria.NO_REQUIREMENT);
        String provider = locationManager.getBestProvider(locationCriteria, true);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            locationManager.requestLocationUpdates(provider, 20000, 0, this);
        }
        Notification notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANEL_ID).setContentTitle("LocationTracker")
                .setContentText("Фоновый режим запущен").setPriority(PRIORITY_DEFAULT)
                .setCategory(NotificationCompat.CATEGORY_SERVICE).build();
        startForeground(1411, notification);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Notification notification = new NotificationCompat.Builder(this, NOTIFICATION_CHANEL_ID).setContentTitle("LocationTracker")
                .setContentText("Фоновый режим запущен").setPriority(PRIORITY_DEFAULT)
                .setCategory(NotificationCompat.CATEGORY_SERVICE).build();
        startForeground(1411, notification);
        if (locationManager == null) {
            locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
            Criteria locationCriteria = new Criteria();
            locationCriteria.setAccuracy(Criteria.NO_REQUIREMENT);
            locationCriteria.setPowerRequirement(Criteria.NO_REQUIREMENT);
            String provider = locationManager.getBestProvider(locationCriteria, true);
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
                    && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
                locationManager.requestLocationUpdates(provider, 20000, 0, this);
            }
        }
        return Service.START_STICKY;
    }

    public void setListener(LocationUpdateListener updateListener) {
        this.updateListener = updateListener;
    }

    @Override
    public void onLocationChanged(@NonNull Location location) {
        if (updateListener != null) {
            locations += formatMillisToHours(System.currentTimeMillis()) + ": " + location.getLatitude() + ", " + location.getLongitude() + "\n";
            updateListener.onLocationUpdated(locations);
        }
    }
    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {

    }

    @Override
    public void onProviderEnabled(@NonNull String provider) {

    }

    @Override
    public void onProviderDisabled(@NonNull String provider) {

    }

    public void removeListener(LocationUpdateListener updateListener) {
        if (this.updateListener != null) {
            this.updateListener = null;
        }
    }

    public class Binder extends android.os.Binder {
        public LocationService getService() {
            return LocationService.this;
        }
    }

    public static String formatMillisToHours(long millis) {
        @SuppressLint("SimpleDateFormat")
        DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
        Date date = new Date(millis);
        return formatter.format(date);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        locationManager.removeUpdates(this);
    }

my application class onCreate:

 @Override
    public void onCreate() {
        super.onCreate();
        NotificationChannel channel;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            channel = new NotificationChannel(LocationService.NOTIFICATION_CHANEL_ID,
                    "LocationTrackerChannel", NotificationManager.IMPORTANCE_DEFAULT);
            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        }
1 Answers

Couple days ago I found an answer, and hope it will help somebody. Problem was in BatterySaver service of Xiaomy device. When I switched it from default 'Battery saver' mode to 'No restrictions' my foreground service begin to recieve location updates. Here is code that provides some oportunity to notify user that your app need to work without restrictions of MIUI BatterySaver:

    public static void checkDevice(final Activity activity){
            String manufacturer = android.os.Build.MANUFACTURER;
            if ("xiaomi".equalsIgnoreCase(manufacturer)) {
                boolean notifiedAboutBatterySave = PreferencesManager.getInstance().isNotifiedAboutBatterySaveMode();
                if (!notifiedAboutBatterySave) {
                    AlertDialog.Builder adb = new AlertDialog.Builder(activity);
                    adb.setPositiveButton("Go to settings", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            try {
                                Intent intent = new Intent();
                                ComponentName componentName = new ComponentName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity");
                                intent.setComponent(componentName);
                                intent.putExtra("package_name", activity.getPackageName());
                                intent.putExtra("package_label", activity.getText(R.string.app_name));
                                activity.startActivityForResult(intent, BATTERY_SAVE_MODE_REQUEST_CODE);
                            } catch (ActivityNotFoundException e) {
                              e.printStackTrace()
                            }
                        }
                    });
                    AlertDialog alert = adb.create();
                    alert.setTitle("Attention");
                    alert.setMessage("message about battery saver mode");
                    alert.setCancelable(true);
                    alert.show();
                }
            }
        }

I didn't find any solution that can help to recognize current mode of BatterySaver either any way to check if user switched this mode or not. That is why I show this alert to user only first time he use my app and pray) If anyone will find any better solution to handle this situation, please, post your answer here, I will mark it as right one.

Related