I am new to android and am working on an Android app that makes measurements every couple of seconds and links these measurements to the current location of the user. The data should still be collected even when the user is using their phone (app is minimized) or when the screen is locked. I should note that this app is meant to be used internally (not on the Google Play Store) and with full permission of the users!
When looking in the Android documentation I found that a foreground service might be the solution for me. So I implemented this service the way it is documented. When programming the app everything seemed to be working fine but when I started to make a release build of the application and was testing the app I noticed some unexpected behavior. This is the code i used for my service and to call the service from my activity:
public class LocationService extends Service {
private final LocationServiceBinder binder = new LocationServiceBinder();
private final String TAG = "LocationService";
private LocationListener mLocationListener;
private LocationManager mLocationManager;
private PowerManager.WakeLock wakeLock;
private Timer timer;
private final int LOCATION_INTERVAL = 500;
private final int LOCATION_DISTANCE = 1;
#Override
public void onCreate() {
startForeground(12345678, getNotification());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Service:WakeLock");
wakeLock.acquire();
startTracking();
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public void onTaskRemoved(Intent rootIntent) {
task.cancel();
wakeLock.release();
if (timer != null) {
timer.cancel();
timer.purge();
timer.cancel();
timer = null;
}
if (mLocationManager != null) {
try {
mLocationManager.removeUpdates(mLocationListener);
} catch (Exception ex) {
Log.i(TAG, "fail to remove location listeners, ignore", ex);
}
}
stopForeground(true);
stopSelf();
super.onTaskRemoved(rootIntent);
}
private Notification getNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel("channel_01",
"My Channel", NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, intent, 0);
return new NotificationCompat.Builder(this, "channel_01")
.setContentTitle("APP")
.setContentText("App is running!")
.setContentIntent(pendingIntent).build();
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class LocationServiceBinder extends Binder {
public LocationService getService() {
return LocationService.this;
}
}
private TimerTask task = new TimerTask() {
#Override
public void run() {
requestLocationInformation();
}
};
public void startTracking() {
initializeLocationManager();
mLocationListener = new LocationListener(LocationManager.GPS_PROVIDER);
if (timer == null) {
timer = new Timer("Measurements");
timer.scheduleAtFixedRate(task, 1000, 5000);
}
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
LOCATION_INTERVAL, LOCATION_DISTANCE, mLocationListener);
} catch (java.lang.SecurityException ex) {
Log.i(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.d(TAG, "gps provider does not exist " + ex.getMessage());
}
}
}
private class LocationListener implements android.location.LocationListener {
private Location lastLocation = null;
private final String TAG = "LocationListener";
private Location mLastLocation;
public LocationListener(String provider) {
mLastLocation = new Location(provider);
}
}
public void requestLocationUpdates() {
try {
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
LOCATION_INTERVAL, LOCATION_DISTANCE, mLocationListener);
} catch (java.lang.SecurityException ex) {
Log.i(TAG, "fail to request location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.d(TAG, "gps provider does not exist " + ex.getMessage());
}
}
private void initializeLocationManager() {
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
private void requestLocationInformation() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
Location location = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (location != null && swbRunning) {
String locInfo = "latitude:" + location.getLatitude() + " longitude:" + location.getLongitude() + " accuracy: " + location.getAccuracy() + "\n";
Log.d(TAG, "Location --> " + locInfo);
}
}
}
}
public class MainActivity extends ReactActivity {
//permissions to be requested at runtime - needed for android 6.0+
static String[] runtimePermissions = { Manifest.permission.INTERNET, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.READ_PHONE_STATE, Manifest.permission.WAKE_LOCK };
static int permissionRequestCode = 1;
private LocationService locaService;
#RequiresApi(api = Build.VERSION_CODES.M)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!havePermissions()) {
requestPermissions(runtimePermissions, permissionRequestCode);
} else {
Intent intent = new Intent(this, LocationService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
startForegroundService(intent);
else
startService(intent);
}
}
private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
locaService = ((LocationService.LocationServiceBinder) service).getService();
}
public void onServiceDisconnected(ComponentName className) {
locaService = null;
}
};
To test the app I used a Samsung Galaxy A42 (android 11) device, this is the device that is also used by our users.
In the release version, everything works fine until the app is minimized or the screen is locked.
I looked into this issue and based on information I could find online about this problem,
I tried some different solutions. I added a partial wake lock to keep the device running smoothly and I also added stopWithTask=false to the android manifest as suggest by the following posts. I also ran the application on an older device (android 8) that did not have this problem.
Keep background service running after killing an application
My Android 11 app stops running when the user puts it into the background unless the phone is connected to power
Android keep screen on on Samsung devices
As mentioned in that last post Samsung is know for performing battery optimizations which may affect apps running foreground services. So I used the advice on https://dontkillmyapp.com/ but this doesn’t solve the issue.
Since I tried every solution I could find, I am starting to think that this might be the main source of my issue. I was wondering if anyone has had a similar issue and if there might be a solution for this problem.
Related
I' trying to implement Google's android foreground location sample from github in my application. It works fine in the first run of the application. It requests for location permission and when the user grants permission, the service is starting flawlessly and fetches location.
But when the app runs the next time while it already has the permission, it crashes by showing a NullPointerException. I'm not able to narrow down the problem. What is different in the second run of the app which already has the permission? Getting error in OnStart() in mService.requestLocationUpdates(); line.
Does it has to do anything with the package name that is declared in the LocationUpdatesService? or is it something else with permission? or with binding?
Stacktrace
2020-10-21 11:58:04.346 31805-31805/com.example.testapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.testapp, PID: 31805
java.lang.NullPointerException: Attempt to invoke virtual method 'void com.example.testapp.LocationUpdatesService.requestLocationUpdates()' on a null object reference
at com.example.testapp.EventsActivity.onStart(EventsActivity.java:237)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1391)
at android.app.Activity.performStart(Activity.java:7348)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3140)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
at
android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
at
android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1950)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7073)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965)
2020-10-21 11:58:04.367 31805-31805/com.example.testapp I/Process: Sending signal. PID: 31805 SIG: 9
MainActivity.java
public class MainActivity extends AppCompatActivity implements
SharedPreferences.OnSharedPreferenceChangeListener {
//Location
private static final String TAG = MainActivity.class.getSimpleName();
// Used in checking for runtime permissions.
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
// The BroadcastReceiver used to listen from broadcasts from the service.
private MyReceiver myReceiver;
// A reference to the service used to get location updates.
private LocationUpdatesService mService = null;
// Tracks the bound state of the service.
private boolean mB
ound = false;
// Monitors the state of the connection to the service.
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocationUpdatesService.LocalBinder binder = (LocationUpdatesService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myReceiver = new MyReceiver();
// Check that the user hasn't revoked permissions by going to Settings.
if (Utils.requestingLocationUpdates(this)) {
if (!checkPermissions()) {
requestPermissions();
}
}
....
.....
}
#Override
protected void onStart() {
super.onStart();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
if (!checkPermissions()) {
requestPermissions();
} else {
mService.requestLocationUpdates(); //CRASHING HERE
}
// Bind to the service. If the service is in foreground mode, this signals to the service
// that since this activity is in the foreground, the service can exit foreground mode.
bindService(new Intent(this, LocationUpdatesService.class), mServiceConnection,
Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
if (mBound) {
// Unbind from the service. This signals to the service that this activity is no longer
// in the foreground, and the service can respond by promoting itself to a foreground
// service.
unbindService(mServiceConnection);
mBound = false;
}
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this);
super.onStop();
}
/**
* Returns the current state of the permissions needed.
*/
private boolean checkPermissions() {
return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
}
private void requestPermissions() {
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.sliding_layout),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
/**
* Callback received when a permissions request has been completed.
*/
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(TAG, "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission was granted.
mService.requestLocationUpdates();
} else {
// Permission denied.
//setButtonsState(false);
Snackbar.make(
findViewById(R.id.sliding_layout),
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.settings, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
})
.show();
}
}
}
/**
* Receiver for broadcasts sent by {#link LocationUpdatesService}.
*/
private class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION);
if (location != null) {
Toast.makeText(MainActivity.this, Utils.getLocationText(location),
Toast.LENGTH_SHORT).show();
}
}
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
}
#Override
protected void onResume() {
LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver,
new IntentFilter(LocationUpdatesService.ACTION_BROADCAST));
super.onResume();
wakeLock.acquire();
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
super.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
if(getBooleanFromSP("logOutBtnClicked")){
stopService();
mService.removeLocationUpdates();
}else if(!getBooleanFromSP("logOutBtnClicked")){
startService();
mService.removeLocationUpdates();
}
}
}
LocationUpdatesService.java
public class LocationUpdatesService extends Service {
private static final String PACKAGE_NAME =
"com.google.android.gms.location.sample.locationupdatesforegroundservice";
private static final String TAG = LocationUpdatesService.class.getSimpleName();
/**
* The name of the channel for notifications.
*/
private static final String CHANNEL_ID = "channel_01";
static final String ACTION_BROADCAST = PACKAGE_NAME + ".broadcast";
static final String EXTRA_LOCATION = PACKAGE_NAME + ".location";
private static final String EXTRA_STARTED_FROM_NOTIFICATION = PACKAGE_NAME +
".started_from_notification";
private final IBinder mBinder = new LocalBinder();
/**
* The desired interval for location updates. Inexact. Updates may be more or less frequent.
*/
private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
/**
* The fastest rate for active location updates. Updates will never be more frequent
* than this value.
*/
private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
UPDATE_INTERVAL_IN_MILLISECONDS / 2;
/**
* The identifier for the notification displayed for the foreground service.
*/
private static final int NOTIFICATION_ID = 12345678;
/**
* Used to check whether the bound activity has really gone away and not unbound as part of an
* orientation change. We create a foreground service notification only if the former takes
* place.
*/
private boolean mChangingConfiguration = false;
private NotificationManager mNotificationManager;
/**
* Contains parameters used by {#link com.google.android.gms.location.FusedLocationProviderApi}.
*/
private LocationRequest mLocationRequest;
/**
* Provides access to the Fused Location Provider API.
*/
private FusedLocationProviderClient mFusedLocationClient;
/**
* Callback for changes in location.
*/
private LocationCallback mLocationCallback;
private Handler mServiceHandler;
/**
* The current location.
*/
private Location mLocation;
public LocationUpdatesService() {
}
#Override
public void onCreate() {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
onNewLocation(locationResult.getLastLocation());
}
};
createLocationRequest();
getLastLocation();
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
mServiceHandler = new Handler(handlerThread.getLooper());
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Android O requires a Notification Channel.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.app_name);
// Create the channel for the notification
NotificationChannel mChannel =
new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
// Set the Notification Channel for the Notification Manager.
mNotificationManager.createNotificationChannel(mChannel);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service started");
boolean startedFromNotification = intent.getBooleanExtra(EXTRA_STARTED_FROM_NOTIFICATION,
false);
// We got here because the user decided to remove location updates from the notification.
if (startedFromNotification) {
removeLocationUpdates();
stopSelf();
}
// Tells the system to not try to recreate the service after it has been killed.
return START_NOT_STICKY;
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mChangingConfiguration = true;
}
#Override
public IBinder onBind(Intent intent) {
// Called when a client (MainActivity in case of this sample) comes to the foreground
// and binds with this service. The service should cease to be a foreground service
// when that happens.
Log.i(TAG, "in onBind()");
stopForeground(true);
mChangingConfiguration = false;
return mBinder;
}
#Override
public void onRebind(Intent intent) {
// Called when a client (MainActivity in case of this sample) returns to the foreground
// and binds once again with this service. The service should cease to be a foreground
// service when that happens.
Log.i(TAG, "in onRebind()");
stopForeground(true);
mChangingConfiguration = false;
super.onRebind(intent);
}
#Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "Last client unbound from service");
// Called when the last client (MainActivity in case of this sample) unbinds from this
// service. If this method is called due to a configuration change in MainActivity, we
// do nothing. Otherwise, we make this service a foreground service.
if (!mChangingConfiguration && Utils.requestingLocationUpdates(this)) {
Log.i(TAG, "Starting foreground service");
startForeground(NOTIFICATION_ID, getNotification());
}
return true; // Ensures onRebind() is called when a client re-binds.
}
#Override
public void onDestroy() {
mServiceHandler.removeCallbacksAndMessages(null);
}
/**
* Makes a request for location updates. Note that in this sample we merely log the
* {#link SecurityException}.
*/
public void requestLocationUpdates() {
Log.i(TAG, "Requesting location updates");
Utils.setRequestingLocationUpdates(this, true);
startService(new Intent(getApplicationContext(), LocationUpdatesService.class));
try {
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.myLooper());
} catch (SecurityException unlikely) {
Utils.setRequestingLocationUpdates(this, false);
Log.e(TAG, "Lost location permission. Could not request updates. " + unlikely);
}
}
/**
* Removes location updates. Note that in this sample we merely log the
* {#link SecurityException}.
*/
public void removeLocationUpdates() {
Log.i(TAG, "Removing location updates");
try {
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
Utils.setRequestingLocationUpdates(this, false);
stopSelf();
} catch (SecurityException unlikely) {
Utils.setRequestingLocationUpdates(this, true);
Log.e(TAG, "Lost location permission. Could not remove updates. " + unlikely);
}
}
/**
* Returns the {#link NotificationCompat} used as part of the foreground service.
*/
private Notification getNotification() {
Intent intent = new Intent(this, LocationUpdatesService.class);
CharSequence text = Utils.getLocationText(mLocation);
// Extra to help us figure out if we arrived in onStartCommand via the notification or not.
intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true);
// The PendingIntent that leads to a call to onStartCommand() in this service.
PendingIntent servicePendingIntent = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// The PendingIntent to launch activity.
PendingIntent activityPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, EventsActivity.class), 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.addAction(R.drawable.ic_launch, getString(R.string.launch_activity),
activityPendingIntent)
.addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates),
servicePendingIntent)
.setContentText(text)
.setContentTitle(Utils.getLocationTitle(this))
.setOngoing(true)
.setPriority(Notification.PRIORITY_HIGH)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker(text)
.setWhen(System.currentTimeMillis());
// Set the Channel ID for Android O.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID); // Channel ID
}
return builder.build();
}
private void getLastLocation() {
try {
mFusedLocationClient.getLastLocation()
.addOnCompleteListener(new OnCompleteListener<Location>() {
#Override
public void onComplete(#NonNull Task<Location> task) {
if (task.isSuccessful() && task.getResult() != null) {
mLocation = task.getResult();
} else {
Log.w(TAG, "Failed to get location.");
}
}
});
} catch (SecurityException unlikely) {
Log.e(TAG, "Lost location permission." + unlikely);
}
}
private void onNewLocation(Location location) {
Log.i(TAG, "New location: " + location);
mLocation = location;
// Notify anyone listening for broadcasts about the new location.
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtra(EXTRA_LOCATION, location);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
// Update notification content if running as a foreground service.
if (serviceIsRunningInForeground(this)) {
mNotificationManager.notify(NOTIFICATION_ID, getNotification());
}
}
/**
* Sets the location request parameters.
*/
private void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
/**
* Class used for the client Binder. Since this service runs in the same process as its
* clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocationUpdatesService getService() {
return LocationUpdatesService.this;
}
}
/**
* Returns true if this is a foreground service.
*
* #param context The {#link Context}.
*/
public boolean serviceIsRunningInForeground(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
Integer.MAX_VALUE)) {
if (getClass().getName().equals(service.service.getClassName())) {
if (service.foreground) {
return true;
}
}
}
return false;
}
}
I am not sure, but try this:
#Override
protected void onStart() {
super.onStart();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
if (!checkPermissions()) {
requestPermissions();
} else if (mService != null) { // add null checker
mService.requestLocationUpdates();
}
// Bind to the service. If the service is in foreground mode, this signals to the service
// that since this activity is in the foreground, the service can exit foreground mode.
bindService(new Intent(this, LocationUpdatesService.class), mServiceConnection,
Context.BIND_AUTO_CREATE);
}
Also:
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocationUpdatesService.LocalBinder binder = (LocationUpdatesService.LocalBinder) service;
mService = binder.getService();
mService.requestLocationUpdates(); // also request it here
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
};
Update code to this code mService is null because you are initilaizing it after mService.requestLocationUpdates();
#Override
protected void onStart() {
super.onStart();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
if (!checkPermissions()) {
requestPermissions();
} else {
bindService(new Intent(this, LocationUpdatesService.class), mServiceConnection,
Context.BIND_AUTO_CREATE);
mService.requestLocationUpdates(); //CRASHING HERE
}
// Bind to the service. If the service is in foreground mode, this signals to the service
// that since this activity is in the foreground, the service can exit foreground mode.
}
bindService(Intent(this, LocationUpdatesService::class.java), mServiceConnection!!,
BIND_AUTO_CREATE)
This code does the trick but somewhere on stackoverflow the preceding code reads like so
if (!checkPermissions()) {
request_permission()
} else{
mService!!.requestLocationUpdates()
}
without the elseif that checks the nullability of the mService - this is the variable that needs to be initiated
I was trying to create a location service which gives location updates when app is in background specially for Android O. I tried many examples available on the internet by creating a foreground service or passing pendingIntent in requestLocationUpdates. But none of them worked. My service stops giving me location update after 10 minutes. I tried to put job scheduler also but that also doesn't work. I found the github project provided by Google this, but those examples are also not working.
Here is my service from one of the above example:
public class LocationUpdatesService extends Service {
private static final String PACKAGE_NAME =
"com.google.android.gms.location.sample.locationupdatesforegroundservice";
private static final String TAG = LocationUpdatesService.class.getSimpleName();
/**
* The name of the channel for notifications.
*/
private static final String CHANNEL_ID = "channel_01";
static final String ACTION_BROADCAST = PACKAGE_NAME + ".broadcast";
static final String EXTRA_LOCATION = PACKAGE_NAME + ".location";
private static final String EXTRA_STARTED_FROM_NOTIFICATION = PACKAGE_NAME +
".started_from_notification";
private final IBinder mBinder = new LocalBinder();
/**
* The desired interval for location updates. Inexact. Updates may be more or less frequent.
*/
private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
/**
* The fastest rate for active location updates. Updates will never be more frequent
* than this value.
*/
private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
UPDATE_INTERVAL_IN_MILLISECONDS / 2;
/**
* The identifier for the notification displayed for the foreground service.
*/
private static final int NOTIFICATION_ID = 12345678;
/**
* Used to check whether the bound activity has really gone away and not unbound as part of an
* orientation change. We create a foreground service notification only if the former takes
* place.
*/
private boolean mChangingConfiguration = false;
private NotificationManager mNotificationManager;
/**
* Contains parameters used by {#link com.google.android.gms.location.FusedLocationProviderApi}.
*/
private LocationRequest mLocationRequest;
/**
* Provides access to the Fused Location Provider API.
*/
private FusedLocationProviderClient mFusedLocationClient;
/**
* Callback for changes in location.
*/
private LocationCallback mLocationCallback;
private Handler mServiceHandler;
/**
* The current location.
*/
private Location mLocation;
public LocationUpdatesService() {
}
#Override
public void onCreate() {
super.onCreate();
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
onNewLocation(locationResult.getLastLocation());
}
};
createLocationRequest();
getLastLocation();
HandlerThread handlerThread = new HandlerThread(TAG);
handlerThread.start();
mServiceHandler = new Handler(handlerThread.getLooper());
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Android O requires a Notification Channel.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.app_name);
// Create the channel for the notification
NotificationChannel mChannel =
new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
// Set the Notification Channel for the Notification Manager.
mNotificationManager.createNotificationChannel(mChannel);
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(TAG, "Service started");
boolean startedFromNotification = intent.getBooleanExtra(EXTRA_STARTED_FROM_NOTIFICATION,
false);
// We got here because the user decided to remove location updates from the notification.
if (startedFromNotification) {
removeLocationUpdates();
stopSelf();
}
// Tells the system to not try to recreate the service after it has been killed.
return START_NOT_STICKY;
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mChangingConfiguration = true;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
// Called when a client (MainActivity in case of this sample) comes to the foreground
// and binds with this service. The service should cease to be a foreground service
// when that happens.
Log.i(TAG, "in onBind()");
stopForeground(true);
mChangingConfiguration = false;
return mBinder;
}
#Override
public void onRebind(Intent intent) {
// Called when a client (MainActivity in case of this sample) returns to the foreground
// and binds once again with this service. The service should cease to be a foreground
// service when that happens.
Log.i(TAG, "in onRebind()");
stopForeground(true);
mChangingConfiguration = false;
super.onRebind(intent);
}
#Override
public boolean onUnbind(Intent intent) {
Log.i(TAG, "Last client unbound from service");
// Called when the last client (MainActivity in case of this sample) unbinds from this
// service. If this method is called due to a configuration change in MainActivity, we
// do nothing. Otherwise, we make this service a foreground service.
if (!mChangingConfiguration && Utils.requestingLocationUpdates(this)) {
Log.i(TAG, "Starting foreground service");
/*
// TODO(developer). If targeting O, use the following code.
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
mNotificationManager.startServiceInForeground(new Intent(this,
LocationUpdatesService.class), NOTIFICATION_ID, getNotification());
} else {
startForeground(NOTIFICATION_ID, getNotification());
}
*/
startForeground(NOTIFICATION_ID, getNotification());
}
return true; // Ensures onRebind() is called when a client re-binds.
}
#Override
public void onDestroy() {
mServiceHandler.removeCallbacksAndMessages(null);
}
/**
* Makes a request for location updates. Note that in this sample we merely log the
* {#link SecurityException}.
*/
public void requestLocationUpdates() {
Log.i(TAG, "Requesting location updates");
Utils.setRequestingLocationUpdates(this, true);
startService(new Intent(getApplicationContext(), LocationUpdatesService.class));
try {
mFusedLocationClient.requestLocationUpdates(mLocationRequest,
mLocationCallback, Looper.myLooper());
} catch (SecurityException unlikely) {
Utils.setRequestingLocationUpdates(this, false);
Log.e(TAG, "Lost location permission. Could not request updates. " + unlikely);
}
}
/**
* Removes location updates. Note that in this sample we merely log the
* {#link SecurityException}.
*/
public void removeLocationUpdates() {
Log.i(TAG, "Removing location updates");
try {
mFusedLocationClient.removeLocationUpdates(mLocationCallback);
Utils.setRequestingLocationUpdates(this, false);
stopSelf();
} catch (SecurityException unlikely) {
Utils.setRequestingLocationUpdates(this, true);
Log.e(TAG, "Lost location permission. Could not remove updates. " + unlikely);
}
}
/**
* Returns the {#link NotificationCompat} used as part of the foreground service.
*/
private Notification getNotification() {
Intent intent = new Intent(this, LocationUpdatesService.class);
CharSequence text = Utils.getLocationText(mLocation);
// Extra to help us figure out if we arrived in onStartCommand via the notification or not.
intent.putExtra(EXTRA_STARTED_FROM_NOTIFICATION, true);
// The PendingIntent that leads to a call to onStartCommand() in this service.
PendingIntent servicePendingIntent = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// The PendingIntent to launch activity.
PendingIntent activityPendingIntent = PendingIntent.getActivity(this, 0,
new Intent(this, MainActivity.class), 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.addAction(R.drawable.ic_launch, getString(R.string.launch_activity),
activityPendingIntent)
.addAction(R.drawable.ic_cancel, getString(R.string.remove_location_updates),
servicePendingIntent)
.setContentText(text)
.setContentTitle(Utils.getLocationTitle(this))
.setOngoing(true)
.setPriority(Notification.PRIORITY_HIGH)
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker(text)
.setWhen(System.currentTimeMillis());
// Set the Channel ID for Android O.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID); // Channel ID
}
return builder.build();
}
private void getLastLocation() {
try {
mFusedLocationClient.getLastLocation()
.addOnCompleteListener(new OnCompleteListener<Location>() {
#Override
public void onComplete(#NonNull Task<Location> task) {
if (task.isSuccessful() && task.getResult() != null) {
mLocation = task.getResult();
} else {
Log.w(TAG, "Failed to get location.");
}
}
});
} catch (SecurityException unlikely) {
Log.e(TAG, "Lost location permission." + unlikely);
}
}
private void onNewLocation(Location location) {
Log.i(TAG, "New location: " + location);
mLocation = location;
// Notify anyone listening for broadcasts about the new location.
Intent intent = new Intent(ACTION_BROADCAST);
intent.putExtra(EXTRA_LOCATION, location);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
// Update notification content if running as a foreground service.
if (serviceIsRunningInForeground(this)) {
mNotificationManager.notify(NOTIFICATION_ID, getNotification());
}
}
/**
* Sets the location request parameters.
*/
private void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
/**
* Class used for the client Binder. Since this service runs in the same process as its
* clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocationUpdatesService getService() {
return LocationUpdatesService.this;
}
}
/**
* Returns true if this is a foreground service.
*
* #param context The {#link Context}.
*/
public boolean serviceIsRunningInForeground(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(
Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(
Integer.MAX_VALUE)) {
if (getClass().getName().equals(service.service.getClassName())) {
if (service.foreground) {
return true;
}
}
}
return false;
}
}
Here is my MainActivity:
public class MainActivity extends AppCompatActivity implements
OnSharedPreferenceChangeListener {
private static final String TAG = MainActivity.class.getSimpleName();
// Used in checking for runtime permissions.
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
// The BroadcastReceiver used to listen from broadcasts from the service.
private MyReceiver myReceiver;
// A reference to the service used to get location updates.
private LocationUpdatesService mService = null;
// Tracks the bound state of the service.
private boolean mBound = false;
// UI elements.
private Button mRequestLocationUpdatesButton;
private Button mRemoveLocationUpdatesButton;
// Monitors the state of the connection to the service.
private final ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocationUpdatesService.LocalBinder binder = (LocationUpdatesService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
mBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myReceiver = new MyReceiver();
setContentView(R.layout.activity_main);
// Check that the user hasn't revoked permissions by going to Settings.
// if (Utils.requestingLocationUpdates(this)) {
if (!checkPermissions()) {
requestPermissions();
}
// }
}
#Override
protected void onStart() {
super.onStart();
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(this);
mRequestLocationUpdatesButton = (Button) findViewById(R.id.request_location_updates_button);
mRemoveLocationUpdatesButton = (Button) findViewById(R.id.remove_location_updates_button);
mRequestLocationUpdatesButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (!checkPermissions()) {
requestPermissions();
} else {
mService.requestLocationUpdates();
}
}
});
mRemoveLocationUpdatesButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mService.removeLocationUpdates();
}
});
// Restore the state of the buttons when the activity (re)launches.
setButtonsState(Utils.requestingLocationUpdates(this));
// Bind to the service. If the service is in foreground mode, this signals to the service
// that since this activity is in the foreground, the service can exit foreground mode.
bindService(new Intent(this, LocationUpdatesService.class), mServiceConnection,
Context.BIND_AUTO_CREATE);
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(myReceiver,
new IntentFilter(LocationUpdatesService.ACTION_BROADCAST));
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(myReceiver);
super.onPause();
}
#Override
protected void onStop() {
if (mBound) {
// Unbind from the service. This signals to the service that this activity is no longer
// in the foreground, and the service can respond by promoting itself to a foreground
// service.
unbindService(mServiceConnection);
mBound = false;
}
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(this);
super.onStop();
}
/**
* Returns the current state of the permissions needed.
*/
private boolean checkPermissions() {
return PackageManager.PERMISSION_GRANTED == ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
}
private void requestPermissions() {
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
Log.i(TAG, "onRequestPermissionResult");
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.length <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(TAG, "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission was granted.
mService.requestLocationUpdates();
} else {
// Permission denied.
setButtonsState(false);
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_denied_explanation,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.settings, new View.OnClickListener() {
#Override
public void onClick(View view) {
// Build intent that displays the App settings screen.
Intent intent = new Intent();
intent.setAction(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null);
intent.setData(uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
})
.show();
}
}
}
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
// Update the buttons state depending on whether location updates are being requested.
if (s.equals(Utils.KEY_REQUESTING_LOCATION_UPDATES)) {
setButtonsState(sharedPreferences.getBoolean(Utils.KEY_REQUESTING_LOCATION_UPDATES,
false));
}
}
/**
* Receiver for broadcasts sent by {#link LocationUpdatesService}.
*/
private class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Location location = intent.getParcelableExtra(LocationUpdatesService.EXTRA_LOCATION);
if (location != null) {
Toast.makeText(MainActivity.this, Utils.getLocationText(location),
Toast.LENGTH_SHORT).show();
}
}
}
private void setButtonsState(boolean requestingLocationUpdates) {
if (requestingLocationUpdates) {
mRequestLocationUpdatesButton.setEnabled(false);
mRemoveLocationUpdatesButton.setEnabled(true);
} else {
mRequestLocationUpdatesButton.setEnabled(true);
mRemoveLocationUpdatesButton.setEnabled(false);
}
}
}
Can someone tell me what will be the correct solution for the background service for Android O.
On Android 10 (API level 29) and higher, you must declare foreground service type.
Need to add android:foregroundServiceType="location" in Service.
<service android:name=".LocationUpdateService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="location"
/>
I have a Handler in my fragment that every 5 second add my location in SQLite.
What I need is to maintain this action in background when I open another fragment. How can I do this ?
Handler timerHandler = new Handler();
Runnable timerRunnable = new Runnable() {
#Override
public void run() {
long millis = System.currentTimeMillis() - startTime;
int seconds = (int) (millis / 1000);
int minutes = seconds / 60;
seconds = seconds % 60;
Point p = ls.getPoint();
String X = Double.toString(p.getX());
String Y = Double.toString(p.getY());
db = new MySQLiteHelper(getActivity());
db.addLocation(new LocationXY(1,X,Y,"111"));
clearmap();
createPolyline();
timerHandler.postDelayed(this, 5000);
}
};
Well, what do both your fragments have in common? Their Activity! So run your handler in it.
As #Piyush pointed out, you should use an AsyncTask or a Service for background processes.
Yeah, as #Piyush and #Ricardo suggest - use a service with a Timer and a LocationListener.
Then invoke timer.scheduleAtFixedRate(yourSqliteUpdateMethod, 0, 5000); to update the database. See my service class below, which contains a lot of extra stuff, but you should be able to get what you want out of it...
public class TrackService extends Service {
/**
* Edit these values to adjust the LOCATION UPDATE polling interval
*/
private static final int LOCATION_INTERVAL_SECONDS = 5;
private static final float LOCATION_DISTANCE_METERS = 10f;
// Static timer variables
private static final long ONE_SECOND_IN_MILLIESECONDS = 1000;
// Create timer and set time as zero
private static Timer timer;
private long timeAsLong = 0;
/**
* Track and submission specific variables
*/
private List<LocationTrack> trackArrayList = new ArrayList<>();
private SubmissionSession submissionSession;
private long submissionID;
private boolean originAdded;
/**
* LocationManager and listener variables
*/
private LocationManager mLocationManager = null;
private LocationListener[] mLocationListeners = new LocationListener[]{
new LocationListener(LocationManager.GPS_PROVIDER)
};
/*private LocationListener[] mLocationListeners = new LocationListener[]{
new LocationListener(LocationManager.GPS_PROVIDER),
new LocationListener(LocationManager.PASSIVE_PROVIDER)
};*/
#Override
public void onCreate() {
//Log.d("onCreate");
timer = new Timer();
initializeLocationManager();
try {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
mLocationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, (LOCATION_INTERVAL_SECONDS * 1000), LOCATION_DISTANCE_METERS,
mLocationListeners[0]);
}
} catch (java.lang.SecurityException ex) {
Log.e("fail to request gps location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.e("gps provider does not exist, " + ex.getMessage());
}
/*try {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
mLocationManager.requestLocationUpdates(
LocationManager.PASSIVE_PROVIDER, (LOCATION_INTERVAL_SECONDS * 1000), LOCATION_DISTANCE_METERS,
mLocationListeners[1]);
}
} catch (java.lang.SecurityException ex) {
Log.e("fail to request passive location update, ignore", ex);
} catch (IllegalArgumentException ex) {
Log.e("passive provider does not exist " + ex.getMessage());
}*/
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
submissionID = prefs.getLong("SUBMISSION_ID", 0);
submissionSession = new SubmissionSession(submissionID);
trackArrayList = new Select().from(LocationTrack.class).where(Condition.column(LocationTrack$Table.SUBMISSIONID).eq(submissionID)).queryList();
originAdded = false;
timeAsLong = prefs.getLong("ELAPSED_TIME_AS_LONG", 0);
} else {
submissionID = intent.getLongExtra("SUB_ID", 0);
submissionSession = new SubmissionSession(submissionID);
// Stick in a reasonably sensible location to indicate to rest of app that a track's
// being captured - this is deleted on 1st real data point from GPS captured
trackArrayList.add(setLocationToLocationTrack(submissionSession.getLocation().getLocation()));
originAdded = true;
timeAsLong = intent.getLongExtra("START_TIME", 0);
}
// Start schedule at one second intervals, calling MainTask
timer.scheduleAtFixedRate(new TrackService.updateTimerAndBroadcastTask(), 0, ONE_SECOND_IN_MILLIESECONDS);
showNotification();
//super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
/**
* This method is necessary to increase the priority of the background services. It appeared that
* some devices, when running eBird Tracks and then used camera, the Tracks service was killed
* by Android due to limited resources. Upping the priority should reduce this.
*/
private void showNotification() {
final int NOTIFICATION_FLAG = 101;
final int ICON_SCALE_DIMENSION = 128;
Intent mainIntent = getPackageManager()
.getLaunchIntentForPackage(getPackageName())
.setPackage(null)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.drawable.lab_logo_square);
PendingIntent pendingMainIntent = PendingIntent.getActivity(this, 0,
mainIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_stat_kiwi_bird1600)
.setLargeIcon(Bitmap.createScaledBitmap(icon, ICON_SCALE_DIMENSION, ICON_SCALE_DIMENSION, false))
.setContentText(getString(R.string.location_and_timer_services_running))
.setContentTitle(getString(R.string.app_name))
.setContentIntent(pendingMainIntent)
.setOngoing(true)
.build();
startForeground(NOTIFICATION_FLAG, notification);
}
#Override
public void onDestroy() {
// Need to call cancel AND purge when the user stops the list
timer.cancel();
timer.purge();
if (mLocationManager != null) {
for (int i = 0; i < mLocationListeners.length; i++) {
try {
mLocationManager.removeUpdates(mLocationListeners[i]);
} catch (Exception ex) {
Log.e("fail to remove location listeners, ignore", ex);
}
}
}
submissionSession.setTracks(trackArrayList);
super.onDestroy();
}
private void initializeLocationManager() {
if (mLocationManager == null) {
mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
private class LocationListener implements android.location.LocationListener {
Location mLastLocation;
public LocationListener(String provider) {
mLastLocation = new Location(provider);
}
#Override
public void onLocationChanged(Location location) {
mLastLocation.set(location);
// Code to add existing location object of site to tracks database on initiation
// to at least give one sensible point. Necessary for assigning the submission as
// an active recording - if no GPS picked up it was being overlooked in the methods
// used to display Checklist in progress throughout app. On first real capture of
// track data, it's deleted
if (originAdded) {
LocationTrack t = trackArrayList.get(0);
t.delete();
trackArrayList.remove(0);
originAdded = false;
}
trackArrayList.add(setLocationToLocationTrack(location));
sendDistanceToSubmissionActivity(Utils.routeDistance((ArrayList) trackArrayList));
}
#Override
public void onProviderDisabled(String provider) {
//Log.d("onProviderDisabled: " + provider);
}
#Override
public void onProviderEnabled(String provider) {
//Log.d("onProviderEnabled: " + provider);
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
//Log.d("onStatusChanged: " + provider);
}
}
// Send an Intent with an action named "tracks_update".
protected void sendDistanceToSubmissionActivity(double distance) {
Intent intent = new Intent("tracks_update");
intent.putExtra("distance", distance);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
/**
* Method to create Locationtrack object and save it to SQLite
*
* #param loc
* #return
*/
protected LocationTrack setLocationToLocationTrack(Location loc) {
LocationTrack l = new LocationTrack();
l.setSubmissionID(submissionID);
l.setLat(loc.getLatitude());
l.setLon(loc.getLongitude());
l.setAccuracy(loc.getAccuracy());
l.setTime(System.currentTimeMillis());
l.setValid(-1); // -1 signifies that the track is active (0 = invalid after editing, 1 = valid)
//Log.d(l.getLat() + " " + l.getLon() + " saved()");
l.save();
return l;
}
/**
* Inner main task, adds a second onto timeAsLong, then sends to SubmissionActivity
*/
private class updateTimerAndBroadcastTask extends TimerTask {
public void run() {
timeAsLong += ONE_SECOND_IN_MILLIESECONDS;
sendDistanceToSubmissionActivity(timeAsLong);
}
// Send an Intent with an action named "timer_update".
protected void sendDistanceToSubmissionActivity(long time) {
//Log.d();
Intent intent = new Intent("timer_update");
intent.putExtra("long_time", time);
LocalBroadcastManager.getInstance(TrackService.this).sendBroadcast(intent);
}
}
}
I'm developing sport tracking app that uses location manager and gps provider for getting location updates every second even if the screen is off and the phone is in the pocket.
When user pressed start button in my activity I start service in foreground, display notification and register location listener.
Service starts receiving location updates and writes them into my track file.
Suddenly I get log message 'Power manager idle mode: true', the phone goes into Doze mode and my sevice stops getting any location update until the phone wakes up.
I read docs about Doze mode and see that it shouldn't affect location services, but it does in my case.
May be I'm doing something wrong. Here is the code of my service, any help is really appreciated.
public class LocService
extends Service
implements LocationListener, GpsStatus.Listener
{
private String mName;
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private LocationManager locationManager;
public LocService(String name)
{
super();
mName = name;
}
private final class ServiceHandler extends Handler
{
public ServiceHandler(Looper looper)
{
super(looper);
}
#Override
public void handleMessage(Message msg)
{
if (msg != null && msg.obj != null)
{
onHandleIntent((Intent)msg.obj);
}
else
{
logMessage("msg for intent is not good");
}
}
}
#Override
public void onCreate()
{
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
logMessage("Enabling Doze mode listener");
IntentFilter filter = new IntentFilter();
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
registerReceiver(new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
onDeviceIdleChanged();
}
}, filter);
}
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
}
#TargetApi(Build.VERSION_CODES.M)
private void onDeviceIdleChanged()
{
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
if(powerManager != null)
{
logMessage("Power manager idle mode: " + powerManager.isDeviceIdleMode());
} else
{
logMessage("Power manager idle changed to ?");
}
}
protected void onHandleIntent(Intent intent)
{
//call start/stop location logging on proper intent
if(intent.getIntExtra("cmd", -1) == 1)
{
startLogging();
} else
{
stopLogging();
}
}
private void startLogging()
{
logMessage("LocationService.startLogging");
try
{
locationManager.addGpsStatusListener(this);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 500, 0, this);
logMessage("requesting gps updates");
startForeground(ONGOING_NOTIFICATION, getNotification(-1, -1, true, false));
logMessage("Sending foreground service notification");
}
catch (SecurityException ex)
{
logMessage(" SecurityException while requesting location info: " + ex);
}
}
private void stopLogging()
{
try
{
locationManager.removeUpdates(this);
stopForeground(true);
notificationManager.cancel(ONGOING_NOTIFICATION);
}
catch (SecurityException ex)
{
logMessage(" SecurityException on stopLogging with location manager: " + ex);
}
}
#Override
public void onLocationChanged(Location location)
{
//save location lat, lon directly to track file
//flush file
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras)
{
//do nothing
}
#Override
public void onProviderEnabled(String provider)
{
logMessage("Location provider enabled " + provider);
}
#Override
public void onProviderDisabled(String provider)
{
logMessage("Location provider disabled " + provider);
}
#Override
public void onGpsStatusChanged(int event)
{
try
{
logMessage(" *** onGpsStatusChanged with " + event);
GpsStatus status = locationManager.getGpsStatus(null);
int inFix = 0;
int total = 0;
for (GpsSatellite satellite : status.getSatellites())
{
if (satellite.usedInFix()) inFix++;
total++;
}
logMessage(" Sats: " + total + " in fix " + inFix);
}
catch (SecurityException sex)
{
}
catch (Exception ex)
{
}
}
#Override
public void onStart(Intent intent, int startId)
{
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
onStart(intent, startId);
return START_STICKY;
}
#Override
public void onDestroy()
{
mServiceLooper.quit();
try
{
locationManager.removeUpdates(this);
}
catch (SecurityException ex)
{
logMessage(" SecurityException on Destroy service with location manager: " + ex);
}
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
private void logMessage(String msg)
{
Log.i("LocServ", msg);
}
}
It is not a given that when ACTION_DEVICE_IDLE_MODE_CHANGED is fired, doze was either turned on or off. There are more factors that can affect idle mode.
Try to create and acquire WakeLock.
PowerManager.WakeLock getLock(Context context, String lockName) {
if (wakeLock == null) {
PowerManager mgr = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName);
}
return wakeLock;
}
//on start service
getLock(ctxt.getApplicationContext(), "lockName").acquire();
//on destroy service
#Override
public void onDestroy() {
PowerManager.WakeLock lock = getLock(this.getApplicationContext(), "lockName");
if (lock.isHeld()) {
lock.release();
}
super.onDestroy();
}
I've an Android 6.0 Application with an unbound android service connecting to Google API for receiving Fused API Location Provider Updates. For this i need the ACCESS_FINE_LOCATION Permission.
Everthing was working fine untill the Update to Marshmallow. Now I need to implement the new Permission Model for my App.
I've read that it is not possible to check android 6.0 Permissions directly from within a service.
All of my Location Update Receiver and Google APi Client connect part is handled by and in the service, and I would linke to keep it there!
Is it possible to check the Permissions once in the acivity and then handle it over to the service when the service is started and the permissions stay in the service for lifetime of the service? Or do I have to check permissions on every LocationUpdate?
And how excactly can I implement the permission check for my service? Has anybody done it yet? Can you give me an Example of your Implementation?
The new Permission check in my activity is already working (like descibed by some examples here on StackOverflow), but how does it work when my service is doing All of the LocationUpdate?
ok update: this is like it is now in my activity but i still receive an error because I#m only checking within my acitvity. How can I get my service part to get aware of the permissions?
my error message:
05-30 15:59:24.035 4261-4261/com.pekam E/Google APi Client﹕ Google
APi Connected Failed.
java.lang.SecurityException: Client must have ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to perform
any location operations.
my activity code:
#Override
protected void onStart() {
super.onStart();
loadPermissions(android.Manifest.permission.ACCESS_FINE_LOCATION, REQUEST_FINE_LOCATION);
if (com.pekam.util.MyAppSettings.isMyServiceRunning(MyService.class, this)) {
Intent intent = new Intent(this, MyService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
} else {
Intent intent = new Intent(this, MyService.class);
startService(intent);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
}
private void loadPermissions(String perm,int requestCode) {
if (ContextCompat.checkSelfPermission(this, perm) != PackageManager.PERMISSION_GRANTED) {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, perm)) {
ActivityCompat.requestPermissions(this, new String[]{perm},requestCode);
}
}
}
service class code:
public class MyService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,LocationListener,AsyncDelegate {
//CurrentUser Object with tracks & everything
public static TblUser user = new TblUser();
public static boolean dataRefresh=false;
private IBinder mBinder = new MyBinder();
private static final int REQUEST_FINE_LOCATION=0;
private NotificationManager nm;
private Timer timer = new Timer();
private LocationRequest mLocationRequest = new LocationRequest();
private String strLOG = "LOG";
private InternetConnectionDetector cd ;
private Boolean isInternetPresent;
//GoogleApiClient
private GoogleApiClient googleApiClient;
#Override
public void onLocationChanged(Location location) {
Location mCurrentLocation = location;
try {
TblGps gps1 = new TblGps();
gps1.setLat(mCurrentLocation.getLatitude());
gps1.setLng(mCurrentLocation.getLongitude());
gps1.setDate(new Timestamp(new Date().getTime()));
gps1.setProvider(mCurrentLocation.getProvider());
gps1.setDeviceID("1");
user.getTracks().get(0).getTblgps().add(gps1);
Log.i("onLocationChanged","new Lat:" + gps1.getLat() +", Lng:"+ gps1.getLng());
Toast.makeText(this, "Location new Location Added", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
#Override
public void onConnectionFailed(ConnectionResult bundle) {
}
#Override
public void onConnected(Bundle bundle) {
Log.i("onConnected", "GoogleApiClient");
try {
Toast.makeText(this, "Location service connected", Toast.LENGTH_SHORT).show();
createLocationRequest();
LocationServices.FusedLocationApi.requestLocationUpdates(
googleApiClient, mLocationRequest, this);
LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
} catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks.
Log.e("Google APi Client", "Google APi Connected Failed.", t);
}
}
#Override
public void onConnectionSuspended(int i) {
}
//Service
#Override
public void onCreate() {
super.onCreate();
cd = new InternetConnectionDetector(getApplicationContext());
isInternetPresent = cd.isConnectingToInternet();
Log.i("MyService", "Service Started.");
showNotification();
getUserObject();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
onTimerTick();
}
}, 60000, 19000L);
try {
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
googleApiClient.connect();
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("MyService", "Received start id " + startId + ": " + intent);
return START_STICKY; // run until explicitly stopped.
}
#Override
public void onDestroy() {
SharedPreferences mPrefs;
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor prefsEditor = mPrefs.edit();
Gson gson = new Gson();
String json = gson.toJson(user);
prefsEditor.putString("MyObject", json);
prefsEditor.commit();
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
// Async Task delegate
#Override
public void executionFinished(HttpRequestTaskGetUser userTask) {
String name= userTask.result.getName();
Log.i("executionFinishedGet",name);
Toast.makeText(this, "Location executionFinishedGet", Toast.LENGTH_SHORT).show();
}
private void checkPermission() {
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
// ActivityCompat.requestPermissions(this,
// new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION,android.Manifest.permission.ACCESS_COARSE_LOCATION},
// REQUEST_FINE_LOCATION);
} else {
}
}
#Override
public void executionFinished(HttpRequestTaskSaveUser userTask) {
String name= userTask.result.getName();
Log.i("executionFinishedSave", name );
Toast.makeText(this, "Location executionFinishedGet", Toast.LENGTH_SHORT).show();
}
private void getUserObject() {
try {
if (isInternetPresent){
HttpRequestTaskGetUser http = new HttpRequestTaskGetUser();
http.delegate=this;
http.execute(user);
}
} catch (Throwable t) {
Log.e("getuserObject", "getuserObject Failed.", t);
}
}
private void saveUserObject() {
try {
if (isInternetPresent) {
HttpRequestTaskSaveUser http = new HttpRequestTaskSaveUser();
http.delegate=this;
http.execute(this.user);
}
} catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks.
Log.e("saveUserObject", "saveUserObject Failed.", t);
}
}
public boolean isRunning()
{
return isMyServiceRunning(this.getClass());
}
private void onTimerTick() {
try {
saveUserObject();
Log.i("TimerTick", "Saved User." );
} catch (Throwable t) { //you should always ultimately catch all exceptions in timer tasks.
Log.e("TimerTick", "Timer Tick Failed.", t);
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
private boolean isGooglePlayServicesAvailable() {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == status) {
return true;
} else {
// GooglePlayServicesUtil.getErrorDialog(status, t, 0).show();
return false;
}
}
private void showNotification() {
nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
// In this sample, we'll use the same text for the ticker and the expanded notification
CharSequence text = getText(R.string.service_started);
// Set the icon, scrolling text and timestamp
Notification notification = new Notification(R.drawable.ic_launcher, text, System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, TabBarActivity.class), 0);
// Set the info for the views that show in the notification panel.
// notification.setLatestEventInfo(this, getText(R.string.service_label), text, contentIntent);
NotificationCompat.Builder builder = new NotificationCompat.Builder(
this);
notification = builder.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.ic_launcher).setTicker(text)//.setWhen(java.util.)
.setAutoCancel(true)//.setContentTitle(title)
.setContentText(text).build();
nm.notify(1, notification);
// notification.contentIntent.
// Send the notification.
// We use a layout id because it is a unique number. We use it later to cancel.
// nm.notify(R.string.service_started, notification);
}
private void createLocationRequest() {
mLocationRequest.setInterval(20000);
mLocationRequest.setFastestInterval(10000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
}
public class MyBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}
}
Yes you can accept the permissions once in activity and use it in service , if you grant the permission , then it will never ask again. Following is the code for showing dialog for ACCESS_FINE_LOCATION ,You can also refer Requesting Permissions at Run Time
private void showPermissionDialog() {
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(mActivity,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(mActivity,
Manifest.permission.ACCESS_FINE_LOCATION)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(mActivity,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
MY_PERMISSIONS_REQUEST_FOR_LOCATION);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
if (mGoogleApiClient != null)
mGoogleApiClient.connect();
}
}
For that you have to check before your service start if your service start before checking permission will not work properly
Use this method. This should work.
On Oncreate() :
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
givePerMisson();
} else {
requestIntent();
}
Add/Delete as per your need:
#TargetApi(Build.VERSION_CODES.M)
private void givePerMisson() {
if ((AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.CALL_PHONE) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.GET_ACCOUNTS) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.CAMERA)
)) {
requestIntent();
} else {
AndyUtils.givePermisson(SplashActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION, AndyUtils.PERMISSOIN);
AndyUtils.givePermisson(SplashActivity.this, Manifest.permission.CALL_PHONE, AndyUtils.PERMISSOIN);
AndyUtils.givePermisson(SplashActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE, AndyUtils.PERMISSOIN);
AndyUtils.givePermisson(SplashActivity.this, Manifest.permission.ACCESS_FINE_LOCATION, AndyUtils.PERMISSOIN);
AndyUtils.givePermisson(SplashActivity.this, Manifest.permission.GET_ACCOUNTS, AndyUtils.PERMISSOIN);
AndyUtils.givePermisson(SplashActivity.this, Manifest.permission.CAMERA, AndyUtils.PERMISSOIN);
if (!AndyUtils.permissionsList.isEmpty()) {
requestPermissions(AndyUtils.permissionsList.toArray(new String[AndyUtils.permissionsList.size()]), AndyUtils.PERMISSOIN_CODE);
}
}
}
3.
#Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case AndyUtils.PERMISSOIN_CODE: {
if ((AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.CALL_PHONE) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.GET_ACCOUNTS) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) &&
AndyUtils.checkPermission(SplashActivity.this, Manifest.permission.CAMERA)
)) {
requestIntent();
} else {
finish();
}
}
}
}
public static boolean checkPermission(Context context, String permission) {
try {
PackageManager pm = context.getPackageManager();
if (pm.checkPermission(permission, context.getPackageName()) == PackageManager.PERMISSION_GRANTED) {
return true;
} else {
}
} catch (Exception e) {
Log.e("PermissionError", e.toString());
}
return false;
}
5.
public static void givePermisson(Context context, String permisson, String permissonType) {
int per = context.checkSelfPermission(permisson);
if (per != PackageManager.PERMISSION_GRANTED) {
permissionsList.add(permisson);
} else if (per != PackageManager.PERMISSION_DENIED) {
}
}