How to access location in background Android 12? - java

I am making an application that fetch last known location when user clicked power key two times. I am using foreground service to register the broadcast receiver and for location I am using fusedlocationproviderclient. The user can fetch the location while app is in background. My problem is I am only able to fetch location for one time only. Second time location is null. How can I solve it?
Note:
It is working fine in android 7 version.
Service:
public class ScreenOnOffBackgroundService extends Service {
private ScreenOnOffReceiver screenOnOffReceiver = null;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
createNotificationChannel();
Intent mainIntent = new Intent(this, DashboardActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,mainIntent, 0);
Notification notification = new NotificationCompat.Builder(this,"safetyId")
.setContentTitle("Safety Service")
.setContentText("Press power key to send alert")
.setSmallIcon(R.drawable.android)
.setContentIntent(pendingIntent)
.build();
startForeground(1,notification);
return START_STICKY;
}
#Override
public void onCreate() {
super.onCreate();
// Create an IntentFilter instance.
IntentFilter intentFilter = new IntentFilter();
// Add network connectivity change action.
intentFilter.addAction("android.intent.action.SCREEN_ON");
// Set broadcast receiver priority.
intentFilter.setPriority(100);
screenOnOffReceiver = new ScreenOnOffReceiver();
HandlerThread broadcastHandlerThread = new HandlerThread("SafetyThread");
broadcastHandlerThread.start();
Looper looper = broadcastHandlerThread.getLooper();
Handler broadcastHandler = new Handler(looper);
registerReceiver(screenOnOffReceiver,intentFilter,null,broadcastHandler);
}
private void createNotificationChannel() {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ){
NotificationChannel channel = new NotificationChannel(
"safetyId",
"Safety Service",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
#Override
public void onDestroy() {
stopForeground(true);
stopSelf();
// Unregister screenOnOffReceiver when destroy.
if(screenOnOffReceiver!=null)
{
unregisterReceiver(screenOnOffReceiver);
}
}
}
Broadcast Receiver:
public class ScreenOnOffReceiver extends BroadcastReceiver {
private int count = 0;
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_SCREEN_ON.equals(action) || Intent.ACTION_SCREEN_OFF.equals(action)) {
count++;
if (count == 2) {
count = 0;
DashboardActivity.getInstance().getLastLocation();
}
}
}
}
Dashboard Activity:
public class DashboardActivity extends AppCompatActivity {
public Button btnAlert;
private static DashboardActivity instance;
private final static int REQUEST_CODE = 123;
private FusedLocationProviderClient locationProviderClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
instance = this;
registerService();
btnAlert = findViewById(R.id.btnAlert);
locationProviderClient = LocationServices.getFusedLocationProviderClient(this);
btnAlert.setOnClickListener(view -> getLastLocation());
}
public static DashboardActivity getInstance(){
return instance;
}
public void registerService(){
if(!foregroundServiceRunning()){
Intent backgroundService = new Intent(this, ScreenOnOffBackgroundService.class);
ContextCompat.startForegroundService(this,backgroundService);
}
}
public boolean foregroundServiceRunning(){
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for(ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)){
if(ScreenOnOffBackgroundService.class.getName().equals(service.service.getClassName())){
return true;
}
}
return false;
}
#SuppressLint("MissingPermission")
public void getLastLocation() {
// check if permissions are given
if (checkPermissions()) {
// check if location is enabled
if (isLocationEnabled()) {
locationProviderClient.getLastLocation().addOnCompleteListener(task -> {
Location location = task.getResult();
if (location == null) {
requestNewLocationData();
} else {
Toast.makeText(this, "Latitude: "+location.getLatitude(), Toast.LENGTH_LONG).show();
}
});
} else {
Toast.makeText(this, "Please turn on your location", Toast.LENGTH_LONG).show();
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
} else {
requestPermissions();
}
}
private boolean checkPermissions() {
return ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
private void requestPermissions() {
String[] permissionArray = new String[]{
android.Manifest.permission.ACCESS_COARSE_LOCATION,
android.Manifest.permission.ACCESS_FINE_LOCATION,};
ActivityCompat.requestPermissions(this, permissionArray, REQUEST_CODE);
}
#SuppressLint("MissingPermission")
private void requestNewLocationData() {
LocationRequest mLocationRequest = LocationRequest.create()
.setInterval(100)
.setFastestInterval(3000)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setMaxWaitTime(100).setNumUpdates(1);
locationProviderClient = LocationServices.getFusedLocationProviderClient(this);
locationProviderClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
}
private final LocationCallback mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
Location mLastLocation = locationResult.getLastLocation();
}
};
private boolean isLocationEnabled() {
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
}
#Override
public void
onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getLastLocation();
}
}
if (requestCode == 0) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
notifyPolice();
}
}
}
}

First off- DashboardActivity.getInstance()- NEVER DO THIS. You're assuming there's exactly 1 instance of an activity at all times. That's wrong. There can be 0. There can be 2. You can't make an activity a singleton. In addition, this always creates a memory leak. NEVER store an Activity in a static variable, this is always a memory leak. It's also (due to the first fact) wrong because it can point to the wrong instance of an Activity.
Secondly- you're using getLastLocation. Don't do that. That function is meant for optimization and will usually return null (I know the docs say the opposite. The docs there have always been wrong.) If you need the location, request it instead. getLastLocation should only be used to get a quick result optimisticly before requesting location updates.
Thirdly- your call to requestNewLocation is passing in a location handler that does nothing. It isn't even updating mLastLocation because that's a method level variable and not a class one.
Fourthly- did you read the new rules for background location processing in ANdroid 12? https://proandroiddev.com/android-12-privacy-changes-for-location-55ffd8c016fd
Those are just the major mistakes, not smaller things that make me go "huh?". With the best of intentions here- this code needs major refactoring. It feels like someone who didn't really understand Android cobbled it together from a dozen examples he didn't understand. If I were you I'd start by simplifying the concept- do one thing, understand it, and then do another. You're overreaching your abilities at the moment doing this all at once, and you'll be better off learning one thing at a time.

Related

Manage External Storage on Android 11 and check permission for Every Activity

How can I check permission for every activity ???
I am trying to make a video player app. I am trying to get External Storage on Android 11 and the lower version. When I am clicking on the button it is asking for permission for both android 11 and the lower version (ex: Kitkat). But the problem is when I am going to the next activity after granting permission and turning off the storage permission from settings in the background. It was not asking for any permission for this new activity.
If anyone has any solution please help me I was shared my code bellow
My permission activity(MainActivity.java) and I want to check permission in (activity_allow_access.java).
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int STORAGE_PERMISSION_CODE = 100;
final static int REQUEST_CODE = 333;
private Button signIn;
public static String PREFS_NAME="MyPrefsFile";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
signIn = findViewById(R.id.button);
signIn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (checkPermission()) {
startActivity(new Intent(MainActivity.this,AllowAccessActivity.class));
finish();
} else {
requestPermission();
}
}
});
}
private void requestPermission(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R){
try {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package",this.getPackageName(),null);
intent.setData(uri);
storageActivityResultLauncher.launch(intent);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
storageActivityResultLauncher.launch(intent);
}
}else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE},STORAGE_PERMISSION_CODE);
}
}
private ActivityResultLauncher<Intent> storageActivityResultLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
#Override
public void onActivityResult(ActivityResult result) {
if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.R){
if(Environment.isExternalStorageManager()){
startActivity(new Intent(MainActivity.this,AllowAccessActivity.class));
finish();
}
else{
Toast.makeText(MainActivity.this, "storage permission required", Toast.LENGTH_SHORT).show();
}
}
}
}
);
public boolean checkPermission(){
if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.R){
return Environment.isExternalStorageManager();
}else {
int write = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
int read = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
return write == PackageManager.PERMISSION_GRANTED && read == PackageManager.PERMISSION_GRANTED;
}
}
private boolean checkStoragePermission(){
boolean result = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == (PackageManager.PERMISSION_GRANTED);
return result;
}
#SuppressLint("MissingSuperCall")
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions,
#NonNull int[] grantResults) {
if (requestCode == STORAGE_PERMISSION_CODE){
if (grantResults.length >0){
boolean write = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean read = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if(write && read) {
startActivity(new Intent(MainActivity.this,AllowAccessActivity.class));
finish();
} else {
requestPermission();
}
}
}
}
#Override
protected void onResume() {
super.onResume();
if (checkPermission()) {
startActivity(new Intent(MainActivity.this,AllowAccessActivity.class));
finish();
}
}
}
AllowAccessActivity.java
public class AllowAccessActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_allow_access);
}
}

Some Times Location Service is not working in background

I used Location Service in my android application, which extends Service and implements the GoogleApiClient, LocationListener. I am updating location onLocationChanged() method and my loaction update interval is 20 second. Sometimes Location Service is not working even though the app is in the background. I want to run my location service always anyhow. Each and every version of Android & every mobile, even though the app is killing from background also.
I call location service using AlarmManager also.
I gave call for location update in onStartCommand() method .
If location service is stopped then it will go to the onDestroy() method or onTaskRemoved() method in that method again I am calling startLocation method like,
startService(new Intent(this, LocationService.class));
My manifest code for service:
<service
android:name=".LocationService"
android:enabled="true"
android:stopWithTask="false"
/>
you can create location service like below.which run on background and foreground mode
public class ServiceLocation extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
private static final String NOTIFICATION_CHANNEL_ID = "my_notification_location";
private static final long TIME_INTERVAL_GET_LOCATION = 1000 * 5; // 1 Minute
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 7; // meters
private Handler handlerSendLocation;
private Context mContext;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 5000;
Location locationData;
#Override
public void onCreate() {
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
// Create the LocationRequest object
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(TIME_INTERVAL_GET_LOCATION) // 3 seconds, in milliseconds
.setFastestInterval(TIME_INTERVAL_GET_LOCATION); // 1 second, in milliseconds
mContext = this;
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
.setOngoing(false)
.setSmallIcon(R.drawable.ic_notification)
.setColor(getResources().getColor(R.color.fontColorDarkGray))
.setPriority(Notification.PRIORITY_MIN);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW);
notificationChannel.setDescription(NOTIFICATION_CHANNEL_ID);
notificationChannel.setSound(null, null);
notificationManager.createNotificationChannel(notificationChannel);
startForeground(1, builder.build());
}
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.w("Service Update Location", "BGS > Started");
if (handlerSendLocation == null) {
handlerSendLocation = new Handler();
handlerSendLocation.post(runnableSendLocation);
Log.w("Service Send Location", "BGS > handlerSendLocation Initialized");
} else {
Log.w("Service Send Location", "BGS > handlerSendLocation Already Initialized");
}
return START_STICKY;
}
private Runnable runnableSendLocation = new Runnable() {
#Override
public void run() {
// You can get Location
//locationData and Send Location X Minutes
if (locationData != null) {
Intent intent = new Intent("GPSLocationUpdates");
intent.putExtra("Latitude", "" + locationData.getLatitude());
intent.putExtra("Longitude", "" + locationData.getLongitude());
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
Log.w("==>UpdateLocation<==", "" + String.format("%.6f", locationData.getLatitude()) + "," +
String.format("%.6f", locationData.getLongitude()));
Log.w("Service Send Location", "BGS >> Location Updated");
}
if (handlerSendLocation != null && runnableSendLocation != null)
handlerSendLocation.postDelayed(runnableSendLocation, TIME_INTERVAL_GET_LOCATION);
}
};
#Override
public void onConnected(#Nullable Bundle bundle) {
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
FusedLocationProviderClient mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
mFusedLocationClient.requestLocationUpdates(mLocationRequest, new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
locationData = locationResult.getLastLocation();
}
}, null);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
if (connectionResult.hasResolution() && mContext instanceof Activity) {
try {
Activity activity = (Activity) mContext;
connectionResult.startResolutionForResult(activity, CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (Exception e) {
e.printStackTrace();
}
} else {
Log.i("", "Location services connection failed with code " + connectionResult.getErrorCode());
}
}
#Override
public void onLocationChanged(Location location) {
Log.w("==>UpdateLocation<==", "" + String.format("%.6f", location.getLatitude()) + "," + String.format("%.6f", location.getLongitude()));
locationData = location;
}
#Override
public void onDestroy() {
if (handlerSendLocation != null)
handlerSendLocation.removeCallbacks(runnableSendLocation);
Log.w("Service Update Info", "BGS > Stopped");
stopSelf();
super.onDestroy();
}
}
Declare this service in android manifest file like below
<service
android:name=".ServiceLocation"
android:enabled="true"
android:exported="true" />
you can start service from activity using this code
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ContextCompat.startForegroundService(MainActivity.this, new Intent(MainActivity.this, ServiceLocation.class));
} else {
startService(new Intent(MainActivity.this, ServiceLocation.class));
}

The first transition is always the same - Activity Recognition API - Activity Transition

I use activity recognition api and activity transitions. When I first run the app, the first transition is always the. In this app it's WALKING-ENTER. When I tried with only IN_VEHICLE-ENTER and IN_VEHICLE-EXIT in transitions, it was IN_VEHICLE-ENTER. I thought about ignoring the first transition but a device I've tested on didn't have such a problem. The devices that had these problem were Android 8.1, and the device didn't have the problem was 6.0.
MainActivity extends AppCompatActivity
private static Intent serviceIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int PERMISSION_ALL = 1;
String[] PERMISSIONS = {Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION};
if(!hasPermissions(this, PERMISSIONS)){
ActivityCompat.requestPermissions(this, PERMISSIONS, PERMISSION_ALL);
}
Button button = findViewById(R.id.button);
button.setOnClickListener(
new View.OnClickListener() {
public void onClick(View view) {
serviceIntent = new Intent(MainActivity.this, ServiceS.class);
MainActivity.this.startService(serviceIntent);
}
});
}
public static boolean hasPermissions(Context context, String... permissions) {
if (permissions != null) {
for (String permission : permissions) {
if (ActivityCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
}
return true;
}
ServiceS extends Service
public ServiceS() {
super();
}
public static void locationArrived(Context context, Location location) {
Log.d("hmm: ", location.toString());
}
#SuppressLint("MissingPermission")
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onCreate() {
initActivityTransition();
super.onCreate();
}
#Override
public void onDestroy() {
Log.d("hmm: ", "Updates stopped!");
Task task = ActivityRecognition.getClient(this)
.removeActivityTransitionUpdates(activityPendingIntent);
super.onDestroy();
}
// ACTIVITY TRANSITION BLOCK START
private static List<ActivityTransition> transitions = new ArrayList<>();
private static PendingIntent activityPendingIntent;
private static ActivityTransitionRequest transitionRequest;
void initActivityTransition(){
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.IN_VEHICLE)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.IN_VEHICLE)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.WALKING)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.STILL)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build());
transitions.add(
new ActivityTransition.Builder()
.setActivityType(DetectedActivity.STILL)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
.build());
Intent activityIntentService = new Intent(this, TransitionReceiver.class);
activityPendingIntent = PendingIntent.getBroadcast(this, 1, activityIntentService, PendingIntent.FLAG_UPDATE_CURRENT);
Log.d("hmm: ","DriveBuddyService - initActivityTransition");
}
static void transitionArrived(final ActivityTransitionEvent event, final Context context){
Log.d("hmm: ", event.toString());
Toast.makeText(context, event.getActivityType() + "-" + event.getTransitionType(), Toast.LENGTH_LONG).show();
}
#Override
public void onStart(Intent intent, int startId) {
if(transitionRequest==null) {
transitionRequest = new ActivityTransitionRequest(transitions);
}
Task task = ActivityRecognition.getClient(this)
.requestActivityTransitionUpdates(transitionRequest, activityPendingIntent);
super.onStart(intent, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
TransitionReceiver extends BroadcastReceiver.
public void onReceive(final Context context, Intent intent) {
Log.d("hmm: ","DriveBuddyTransitionReceiver - Enter");
if (ActivityTransitionResult.hasResult(intent)) {
ActivityTransitionResult result = ActivityTransitionResult.extractResult(intent);
for (ActivityTransitionEvent event : result.getTransitionEvents()) {
ServiceS.transitionArrived(event, context);
}
}
}
I observed the same thing too. It seems that the last activity is kept in cache or something like that. I saw this happening on Android 7.1 and Android 8.1.
To counter this I check the elapsed time of when the transition happened. I ignore the callback if the transition happened over 30 seconds ago.
This is what my code looks like:
#Override
public void onReceive(Context context, Intent i) {
if (ActivityTransitionResult.hasResult(i)) {
ActivityTransitionResult result = ActivityTransitionResult.extractResult(i);
for (ActivityTransitionEvent event : result.getTransitionEvents()) {
//continue only if the activity happened in the last 30 seconds
//for some reason callbacks are received for old activities when the receiver is registered
if(((SystemClock.elapsedRealtime()-(event.getElapsedRealTimeNanos()/1000000))/1000) <= 30) {
//activity transition is legit. Do stuff here..
}
}
}
}
I've typically seen callbacks arrive 0-10 seconds of them occurring.

Android - location service not running in background, only when connected to the computer

I have created a service that has a LocationListener.
In the Fragment i have a BroadcastReceiver that successfully receives location from the Service. Everything works fine when the phone is connected to my computer and I have pressed "run" (not debug mode). I receive location continuously when the screen on my phone is turned off as I can see in the "Run" console in Android Studio.
However when i disconnect my phone from the computer and turn the screen off, I stoped receiving location updates. Or I got only one or two location updates before it stoped. Then when turning the screen on again the Service starts sending location updates.
I have tried using a WakeLock on the Fragment and the Service, but it dosent seem to have any effect.
Can someone help me please!?... :,(
In my Service I set a ArrayList->String in putExtra() that contains a Latitude and a TimeStamp so that I can see if the locationlistener did update location when screen is locked. And in the Fragment I display the results in the view so that I can manually debug.
Here is my Service code:
public class GpsService extends Service {
private LocationListener listener;
private LocationManager locationManager;
private Intent mIntent;
private ArrayList<String> latTimeArrayList = new ArrayList<>();
private PowerManager.WakeLock wakeLock;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
mIntent = new Intent("location_update");
PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
wakeLock.acquire();
return START_NOT_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
listener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
mIntent.putExtra("Latitude", location.getLatitude());
mIntent.putExtra("Longitude", location.getLongitude());
if(latTimeArrayList.size() < 10){
latTimeArrayList.add(location.getLatitude() + " -> " + new Date());
}else if(latTimeArrayList.size() == 10){
mIntent.putExtra("latTimeArray", latTimeArrayList);
}
sendBroadcast(mIntent);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
}
};
//noinspection MissingPermission
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 100, 0, listener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 100, 0, listener);
}
#Override
public void onDestroy() {
super.onDestroy();
if(locationManager != null){
//noinspection MissingPermission
locationManager.removeUpdates(listener);
wakeLock.release();
}
}
}
private BroadcastReceiver broadcastReceiver;
ArrayList<String> aString = new ArrayList<>();
private String mmtext = "";
TextView debugtext = (TextView) mView.findViewById(R.id.hasCompletedTrack);
int doagain = 0;
#Override
public void onResume() {
super.onResume();
if(broadcastReceiver == null){
broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
try{
if (doagain == 0){
aString = (ArrayList<String>)intent.getExtras().get("latTimeArray");
if(aString != null || aString.size() != 0){
for (int i =0; i< aString.size(); i++){
mmtext += aString.get(i) + "\n";
}
doagain = 1;
debugtext.setText(mmtext);
}
}
}catch(NullPointerException e){}
I found the answer...
I had to turn off PoweSaving mode on my Samsung Galaxy S6 edge.
Also, in my broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
that's in my Fragment, I did some position calculations for every location update that I got from my Service with a Intent getExtra() (this is not in the code I posted). However onReceive() only gets triggered when the screen is on(not locked). Therefore my calculations in the fragment were never triggered, only when the screen was on it triggered the calculation methods.
My solution to this was to handle all the calculations directly in my service.

Waiting until location is on after turning it on through location services alert

I am having a problem where if my phone's location services is turned off, I want to set an alert to open the location services settings and turn it on there. This works fine but the issue is when I navigate back to my application after turning it on. It seems like when I click back, the onResume of my activity is called but the location is still not set correctly there until onResume is completed. Here is my code:
Comments.java:
#Override
public void onResume() {
super.onResume();
locationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
Location location = LocationService.getLastLocation();
if(location == null) {
if(getLastKnownLocation() == null) {
if(checkLocationEnabled()); {
location = getLastKnownLocation();
}
}
}
if(location != null) {
progressOverlay = fragmentView.findViewById(R.id.progress_overlay);
AndroidUtils.animateView(progressOverlay, View.VISIBLE, 0.9f, 200);
//Within this function call, the progress overlay is set to View.GONE.
databaseQuery.getPublicPosts(location, progressOverlay, fragmentView, getContext());
} else {
//We need to handle an error saying that location is not enabled for this device and exit them out of the app
}
}
private Location getLastKnownLocation() {
locationManager = (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE);
List<String> providers = locationManager.getProviders(true);
Location bestLocation = null;
for (String provider : providers) {
if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return null;
} else {
Location l = locationManager.getLastKnownLocation(provider);
if (bestLocation == null || l.getAccuracy() < bestLocation.getAccuracy()) {
// Found best last known location: %s", l);
bestLocation = l;
}
}
}
return bestLocation;
}
public boolean checkLocationEnabled() {
try {
gps_enabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch(Exception ex) {
}
try {
network_enabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
} catch(Exception ex) {
}
if(!gps_enabled && !network_enabled) {
// notify user
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
dialog.setMessage(getContext().getResources().getString(R.string.gpsNetworkNotEnabled));
dialog.setPositiveButton(getContext().getResources().getString(R.string.openLocationSettings), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface paramDialogInterface, int paramInt) {
Intent myIntent = new Intent( Settings.ACTION_LOCATION_SOURCE_SETTINGS);
getContext().startActivity(myIntent);
//get gps
}
});
dialog.setNegativeButton(getContext().getString(R.string.cancel), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface paramDialogInterface, int paramInt) {
}
});
dialog.show();
}
return gps_enabled || network_enabled;
}
What I do is when the phone location services is off, I set up an alert that takes you to the location services screen. When the user turns on the location services screen, if he or she clicks "back" on his or her phone, they should be taken back to the initial screen that showed that their location services was off alert. However, when navigating back to that screen, my progressOverlay variable is still visible because the progressOverlay is set to View.GONE when the databaseQuery.getPublicPosts call goes through. That doesn't seem to happen because the if(location != null) statement is probably false right when we navigate back to the screen, but is probably set to true after the phone waits a few seconds and fully obtains the phone's location. It's interesting because if I put a debugger anywhere in that code, the location services will be set correctly and my progressOverlay will go away because the location gets set while I am debugging the code. Is there any way I can "wait" or continue to show the loading screen until the location services is fully on before calling databaseQuery.getPublicPosts? Any help would be appreciated.
hi why trying to use getLastKnownLocation.It return your previous latitude and longitude and it might take some time to update lastknown location.
Most probably it retrun you null after on your location.
I'm just referring you google Fused api for location any kind of updation or current.Its very accurate.
How you can fused api in your project.See i'll give you small example.
Step 1. Make this class GoogleLocationService.java
public class GoogleLocationService {
private GoogleServicesCallbacks callbacks = new GoogleServicesCallbacks();
LocationUpdateListener locationUpdateListener;
Context activity;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 30000;
public GoogleLocationService(Context activity, LocationUpdateListener locationUpdateListener) {
this.locationUpdateListener = locationUpdateListener;
this.activity = activity;
buildGoogleApiClient();
}
protected synchronized void buildGoogleApiClient() {
//Log.i(TAG, "Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(activity)
.addConnectionCallbacks(callbacks)
.addOnConnectionFailedListener(callbacks)
.addApi(LocationServices.API)
.build();
createLocationRequest();
mGoogleApiClient.connect();
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
private class GoogleServicesCallbacks implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
#Override
public void onConnected(Bundle bundle) {
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
if (connectionResult.getErrorCode() == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) {
Toast.makeText(activity, "Google play service not updated", Toast.LENGTH_LONG).show();
}
locationUpdateListener.cannotReceiveLocationUpdates();
}
#Override
public void onLocationChanged(Location location) {
if (location.hasAccuracy()) {
if (location.getAccuracy() < 30) {
locationUpdateListener.updateLocation(location);
}
}
}
}
private static boolean locationEnabled(Context context) {
boolean gps_enabled = false;
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
try {
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
ex.printStackTrace();
}
return gps_enabled;
}
private boolean servicesConnected(Context context) {
return isPackageInstalled(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, context);
}
private boolean isPackageInstalled(String packagename, Context context) {
PackageManager pm = context.getPackageManager();
try {
pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
return true;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return false;
}
}
public void startUpdates() {
/*
* Connect the client. Don't re-start any requests here; instead, wait
* for onResume()
*/
if (servicesConnected(activity)) {
if (locationEnabled(activity)) {
locationUpdateListener.canReceiveLocationUpdates();
startLocationUpdates();
} else {
locationUpdateListener.cannotReceiveLocationUpdates();
Toast.makeText(activity, "Unable to get your location.Please turn on your device Gps", Toast.LENGTH_LONG).show();
}
} else {
locationUpdateListener.cannotReceiveLocationUpdates();
Toast.makeText(activity, "Google play service not available", Toast.LENGTH_LONG).show();
}
}
//stop location updates
public void stopUpdates() {
stopLocationUpdates();
}
//start location updates
private void startLocationUpdates() {
if (checkSelfPermission(activity, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(activity, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, callbacks);
}
}
public void stopLocationUpdates() {
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, callbacks);
}
}
public void startGoogleApi() {
mGoogleApiClient.connect();
}
public void closeGoogleApi() {
mGoogleApiClient.disconnect();
}
}
Step2. Make this interface
LocationUpdateListener.java
public interface LocationUpdateListener {
/**
* Called immediately the service starts if the service can obtain location
*/
void canReceiveLocationUpdates();
/**
* Called immediately the service tries to start if it cannot obtain location - eg the user has disabled wireless and
*/
void cannotReceiveLocationUpdates();
/**
* Called whenever the location has changed (at least non-trivially)
* #param location
*/
void updateLocation(Location location);
/**
* Called when GoogleLocationServices detects that the device has moved to a new location.
* #param localityName The name of the locality (somewhere below street but above area).
*/
void updateLocationName(String localityName, Location location);
}
Step 3. Use this piece of code where you want to get location
public class MainActivity extends ActionBarActivity {
private GoogleLocationService googleLocationService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() {
#Override
public void canReceiveLocationUpdates() {
}
#Override
public void cannotReceiveLocationUpdates() {
}
//update location to our servers for tracking purpose
#Override
public void updateLocation(Location location) {
if (location != null ) {
Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());
}
}
#Override
public void updateLocationName(String localityName, Location location) {
googleLocationService.stopLocationUpdates();
}
});
googleLocationService.startUpdates();
}
#Override
public void onDestroy() {
super.onDestroy();
if (googleLocationService != null) {
googleLocationService.stopLocationUpdates();
}
}
}
Updated answer
how to send and receive lat lng through broadcast
call this onlocationchanged
Intent sendBroadIntentPND = new Intent("COORD_BROADCAST_FILTER");
sendBroadIntentPND.putExtra("LOC_LAT", location.getLatitude());
sendBroadIntentPND.putExtra("LOC_LON", location.getLongitude());
getApplicationContext().sendBroadcast(sendBroadIntentPND);
then where you want to receive the location call this way
// declear global variable
private final String COORD_BROADCAST_FILTER = "COORD_BROADCAST_FILTER";
private Location newLocation;
private BroadcastReceiver coordinateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
try {
newLocation.setLatitude(intent.getDoubleExtra("LOC_LAT", 0.0));
newLocation.setLongitude(intent.getDoubleExtra("LOC_LON", 0.0));
} catch (Exception e) {
e.printStackTrace();
}
}
};
Register you receiver oncreate,
getActivity().registerReceiver(coordinateReceiver, new IntentFilter(COORD_BROADCAST_FILTER));
unregister your receiver ondestroy
getActivity().unregisterReceiver(coordinateReceiver);
Thanks hope this help you.
First you have an unwanted semi-colon in if(checkLocationEnabled()); which acts as an empty statement causing the second call to getLastKnownLocation() to always execute (this will most probably not affect the problem).
The LocationManager class has a constant KEY_STATUS_CHANGED that is the key of a bundle extra that is passed when a status change is broadcast. See the documentation related to the constant. To trigger this change you need to call [LocationManager.requestLocationUpdates](https://developer.android.com/reference/android/location/LocationManager.html#requestLocationUpdates(long, float, android.location.Criteria, android.app.PendingIntent)) so that the location manager receives such a status update.
From the documentation of this method, you can either use a BroadcastReceiver to receive the update by filtering on the above constant. Here is a related post that does this. Or you can use another overloaded version of the request updates method that takes a LocationListener which has an onStatusChanged callback.
A similar update exists when a location provider is enabled, with the KEY_PROVIDER_ENABLED. You can try listening for both updates if the status update didn't work.
Note that when requesting location updates for providers, in order to include updates for all providers including disabled ones, you should retrieve them using LocationManager.getProviders(false).
Once you receive the appropriate update events, you can call removeUpdates to remove the update requests.
Please take note that
The Google Play services location APIs are preferred over the Android
framework location APIs (android.location) as a way of adding location
awareness to your app. If you are currently using the Android
framework location APIs, you are strongly encouraged to switch to the
Google Play services location APIs as soon as possible.

Categories

Resources