I'm create background service to update location. It work well with wifi in sleep mode but when i use mobile network in sleep mode, my background service not update location. What should i do to run background service with mobile network in sleep mode?
I using Service library and update location to server.
import android.Manifest;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
public class LocationService extends Service {
double latitude=0f;
double longitude=0f;
private LocationManager locationManager = null;
int LOCATION_INTERVAL = 300000;
float LOCATION_DISTANCE = 0f;
public ServerHelper serverHelper = new ServerHelper();
#Override
public void onCreate() {
Log.d("onCreate: ", "create");
initializeLocationManager();
try {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
LOCATION_INTERVAL,
LOCATION_DISTANCE,
locationListeners[0]);
} catch (java.lang.SecurityException ex) {
} catch (IllegalArgumentException ex) {
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.d("onBind: ", "bind");
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("onStartCommand: ", "startcommand");
super.onStartCommand(intent,flags,startId);
return START_STICKY;
}
#Override
public void onDestroy() {
Log.d("onDestroy: ", "destroy");
super.onDestroy();
if (locationManager != null) {
for (LocationListener locationListener : locationListeners)
{
try {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) !=
PackageManager.PERMISSION_GRANTED) {
return;
}
locationManager.removeUpdates(locationListener);
} catch (Exception ex) {
}
}
}
}
private class LocationListener implements
android.location.LocationListener {
Location mLocation;
Calendar calendar;
DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss",
Locale.getDefault());
#Override
public void onLocationChanged(Location location) {
Log.d("onLocationChanged: ", "locationChange");
calendar = Calendar.getInstance();
Date date = calendar.getTime();
String time = dateFormat.format(date);
mLocation.set(location);
latitude = location.getLatitude();
longitude = location.getLongitude();
String latlng = latitude+","+longitude;
serverHelper.updateLocation(getApplicationContext(),latlng,time);
}
#Override
public void onStatusChanged(String provider, int status, Bundle
extras) {
Log.d("onStatusChanged: ", "status");
}
#Override
public void onProviderEnabled(String provider) {
Log.d("onProviderEnabled: ", "proEnabled");
}
#Override
public void onProviderDisabled(String provider) {
Log.d("onProviderDisabled: ", "proDisabled");
}
LocationListener(String provider) {
mLocation = new Location(provider);
}
}
LocationListener[] locationListeners = new LocationListener[] {
//new LocationListener(LocationManager.GPS_PROVIDER),
new LocationListener(LocationManager.NETWORK_PROVIDER)
};
private void initializeLocationManager() {
if (locationManager == null) {
locationManager = (LocationManager)
getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
}
}
}
when i use mobile network in sleep mode. onLocationChanged can't call
You should not use a background service for something like that. Modern Android versions will kill the service after 15 minutes and you can't restart it unless your app is running in the foreground. Android tries to preserve battery life and you should try to support it.
Here are possible solutions:
Use AlarmManager to setup a repeating alarm. Android may decide to delay your alarm in certain situations, e.g. if the phone is sleeping.
Use JobScheduler to schedule a recurring job which updates the location. This requires Android L.
Use the Android Jetpack WorkManager. This combines the above two solutions and picks the best way under the hood. This is the preferred solution. Check this page out to see how to schedule recurring work.
If you really want to use a background service for continuously updating the location, you need to create foreground service by calling startForeground() in your service and passing a notification. This notification will be shown to the user as long as your service is running but Android won't kill it. This solution is not recommended as it drains the battery faster and the user will notice due to the ongoing notification.
The main pattern is this: don't keep your app running but schedule the location updates with one of the above solutions. Android became very restrictive with background services over the last couple of versions and forces you to adopt the new mechanics by killing your background services and not letting allow you to restart them unless your app is in the foreground. This preserves battery. I'd recommend to not update the location more than every 15 minutes.
Related
I am running a telemetry service in my app. This service is checking for changes in WiFi environment once a second and if changes are detected it notifies a remote db. I need this service to continue running even if the app is swiped from recent apps or closed.
I followed Continue Service even if application is cleared from Recent app and it seems to work on many devices except for Xiaomi. I've tried many things advised for this device:
enable auto-start.
disable battery optimization for that app.
Tried using a broadcast receiver to start the service
Tried using timer thread and scheduleExecutorservice instead of runnable. didn't make a difference.
This is my latest sample code :
package com.example.csi1;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.widget.Toast;
public class MainService extends Service {
public Context context = this;
public Handler handler = null;
public static Runnable runnable = null;
public static int count = 0;
public int restart = 0;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
Toast.makeText(this, "MainService onStartCommand", Toast.LENGTH_LONG).show();
Bundle extras = intent.getExtras();
count = 0;
handler = new Handler();
runnable = new Runnable(){
public void run() {
count = count+1;
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo;
wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo.getSupplicantState() == SupplicantState.COMPLETED) {
String ssid = wifiInfo.getSSID();
Log.i("Service", "SSID:"+ssid);
Toast toast = Toast.makeText(context, "Service iter " + String.valueOf(count)+ " " + ssid, Toast.LENGTH_LONG );
toast.setGravity(Gravity.TOP|Gravity.RIGHT, 0, 0);
toast.show();
}
handler.postDelayed(runnable, 1000);
}
};
handler.postDelayed(runnable, 3000);
return START_STICKY;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Toast.makeText(this, "onTaskRemoved", Toast.LENGTH_LONG).show();
if (restart == 0) {
PendingIntent service = PendingIntent.getService(
getApplicationContext(),
1001,
new Intent(getApplicationContext(), MainService.class),
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service);
restart=1;
Toast.makeText(this, "Service restarted", Toast.LENGTH_LONG).show();
}
}
#Override
public void onDestroy(){
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
if (restart == 0) {
PendingIntent service = PendingIntent.getService(
getApplicationContext(),
1001,
new Intent(getApplicationContext(), MainService.class),
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service);
restart=1;
Toast.makeText(this, "Service restarted", Toast.LENGTH_LONG).show();
}
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
it runs fine on Samsung device and google nexus emulator, but whenever i run it on Red mi and swipe the app from recent apps,app dies and service with it. Is there a solution for Red mi devices or this is some well known limitation ?
Xiaomi's MIUI and a couple of others are very aggressive on removing background services. As a user you are able to disable it following these steps (for xiaomi). But I am not aware of a solution to circumvent it as a developer.
So you're not doing anything wrong, some phones just don't like background services taking battery life. Similar discussion can be found here: "Protected Apps" setting on Huawei phones, and how to handle it. Their solution is basically having the user disable it by calling the optimization manager for each of the problematic devices
My issue: I am attempting to create an app that can be used university wide. It requires constant access to location. I followed a tutorial online and completed the code underneath. This code works perfectly for my android emulator, but does not work on my device. I get the permission request on my device, but nothing gets updated. Now, the strange thing is, if I use a mock location app on my device, this app DOES work. But only with the mock location app.
Does anyone have any suggestions on how to fix this?
(The xml file is just a single textview. Like i said, I was just testing this first)
package com.example.dylan.locationexample;
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private LocationManager locationManager;
private LocationListener locationListener;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
locationManager = (LocationManager) this.getSystemService(LOCATION_SERVICE);
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
Log.d("Location: ", location.toString());
TextView tv = findViewById(R.id.textView);
tv.setText(tv.getText() + Double.toString(location.getLatitude()) + "\t" + Double.toString(location.getLongitude()) + "\n");
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onProviderDisabled(String provider) {
}
};
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},1);
}else{
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(grantResults.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
if(ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
}
}
}
}
Please ensure you have the following in the manifest
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
And enable gps on your physical device.
If you still face the problem, check the permission settings on your mobile phone for the app. You may have denied permission to access location.
Here is my code am using:
locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
com.google.android.gms.location.LocationListener mLocationListener = new com.google.android.gms.location.LocationListener() {
#Override
public void onLocationChanged(final Location location) {
listener.onLocation(location);
}
};
if(location != null) {
listener.onLocation(location);
}else{
locationListener = new android.location.LocationListener() {
#Override
public void onLocationChanged(Location location) {
listener.onLocation(location);
locationManager.removeUpdates(locationListener);
Log.d(TAG,"Location: " + String.valueOf(location.getLongitude()));
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
}
};
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
}
I'm actually experiencing the same thing and I found a solution to make it work on the physical device. Disable the WI-FI connection and it works. Idk how or why it works though. Now I just opened back the WI-Fi connection and it's working again. If the Wi-fi thing doesn't work for you try restarting the device.
You might also need to wait for a bit before it starts getting the location while moving around the house so it constantly changes.
I made a simple app with geolocation that displays current location of the user in text views, like latitude, longitude and with Geocoder cityname countryname postal-code, etc.
Everything works perfectly in the emulator but for some reason the location doesn't get retrieved in my mobile.
The emulator is running android 7.1 and my mobile is running android 7.0 but that should not be a problem because I made the app with 6.0 marshmallow in mind.
Here is the code
"Mainactivity"
package com.example.slimshady.LocationInfoApp;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.util.List;
import java.util.Locale;
public class MainActivity extends AppCompatActivity {
LocationManager locationManager;
LocationListener locationListener;
Geocoder geocoder;
TextView latitude, longitude, city, postcode, country;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
latitude = (TextView)findViewById(R.id.latitude);
longitude = (TextView) findViewById(R.id.longitude);
city = (TextView)findViewById(R.id.city);
postcode = (TextView)findViewById(R.id.postcode);
country = (TextView)findViewById(R.id.country);
locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
locationListener = new LocationListener() {
#Override
public void onLocationChanged(Location location) {
updateLocationInfo(location);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
#Override
public void onProviderEnabled(String s) {
}
#Override
public void onProviderDisabled(String s) {
}
};
// asking permission starts here
if (Build.VERSION.SDK_INT < 23){
// api level lower than 23 so no need to ask for permission
try {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0,0, locationListener);
}
catch (SecurityException e){
e.printStackTrace();
}
}
else {
// api level greater than or equal to 23 checking if has permission if not VVVV this condition
// means go ask for permission
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 0);
}
else{
// means api level greater than or equal to 23 and already has permission
// so no need to go out and ask for permission
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0,0, locationListener);
// last known location because we went to this part means we have been here before
Location lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
if (lastKnownLocation != null) {
updateLocationInfo(lastKnownLocation);
}
}
}
// asking permission ends here
}
public void updateLocationInfo(Location location) {
geocoder = new Geocoder(getApplicationContext(), Locale.getDefault());
try {
List<Address> locationAddress = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(),1);
if (locationAddress != null && locationAddress.size() > 0) {
latitude.setText("Latitude: "+String.valueOf(locationAddress.get(0).getLatitude()));
longitude.setText("Longitude: "+String.valueOf(locationAddress.get(0).getLongitude()));
city.setText("City: "+String.valueOf(locationAddress.get(0).getLocality()));
postcode.setText("Post Code: "+String.valueOf(locationAddress.get(0).getPostalCode()));
country.setText("Country: "+String.valueOf(locationAddress.get(0).getCountryName()));
}
}
catch (Exception e){
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
startListening();
}
}
public void startListening(){
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0,0, locationListener);
}
}
}
The code is simple and basic, everything should work fine but for some reason it doesn't.
I mean the GPS receiver in my physical device works with google-maps so I know its not broken.
See it works in the emulator fine
But the same code does not work on my mobile and I have added all the permissions:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET"/>
See the same app doesn't work on my physical device, this is a screenshot from my physical device
As you see the location is on and I have granted permission when asked in the popup.
And last but not least to show that the GPS receiver works and is not damaged in my physical device.
Screenshot of me using google maps
I circled in red the GPS on and that maps can get my GPS location
If you are using released apk in physical device then you have to add SHA1 for release apk in restriction section of Google API console . I think it helps .
If not, then please show your logcat error by debugging app in physical device.
There are multiple ways that your device can locate itself. These are frequently confused as "GPS" when they are not.
GPS (Global Positioning System) This is a Global Navigation Satellite System (GNSS) which relies on signals from an orbiting constellation of satellites to locate the device. GPS specifically refers to the constellation of satellites deployed by the United States Department of Defense. Alternatives include GLONASS (Russian) and Galileo (European).
Network This uses WiFi or Bluetooth signals to locate your device. The device scans for transmitters IDs, then sends those IDs to a server over its internet connection to lookup in a database. The server responds with a location.
Your code currently uses only LocationManager.GPS_PROVIDER, which uses only satellites to locate your device. Google Maps may be using Network location, which can still work when GPS is disabled.
I suspect you need to either:
Add LocationManager.NETWORK_PROVIDER
or
Enable GPS on your device. This is somewhere under Settings and Location.
I resolved issue like yours totally removed location manager, and make all location work based on FusedLocationProviderClient, also I was have some strange bug on samsung s8 with LocationSettingsRequest that can start default dialog to turn on gps, it was worked with bugs but after removing locationManager.requestLocationUpdates it works perfectlly with fused.
The main thing i just want to do is that i have to check on me webservice when my application is closed when it gets any alert on webservice then it shows notification or activity.
I read about services and make an example but when i remove the application from recent apps then some time the service is restarted or some the service got killed to.
Code:
package com.usamaakmal.startedservice;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
import android.widget.Toast;
public class NewService extends Service {
public NewService() {
}
int i = 0;
Handler handler = new Handler();
Thread runnable = new Thread() {
#Override
public void run() {
i++;
Toast.makeText(getBaseContext(), "Hello World! " + i, Toast.LENGTH_SHORT).show();
handler.postDelayed(this,1000);
}
};
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();
handler.postDelayed(runnable,2000);
}
#Override
public void onDestroy() {
Toast.makeText(this, "Service Destroyed", Toast.LENGTH_SHORT).show();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
}
Removing app from recent apps list kills app process. Returning START_STICKY from onStartCommand() means you want the system to restart the service after it was killed (either because you removed the app from recents list or system killed service because it ran out of resources).
If you want to keep your service (and it's process) alive you will need foreground service.
System tries to keep foreground services alive as long as possible.
See: Running service in foreground
This question is directly related to the same prevailing stackoverflow question at "Android: get current location of user without using gps or internet" where the accepted answer is actually not answering the question.
I should be able to get the current location name (eg:city name, village name) of the device via network provider not with GPS or internet.
Following is the accepted answer in that question. (The following code parts should be included in the onCreate() method)
// Acquire a reference to the system Location Manager
LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
// Called when a new location is found by the network location provider.
makeUseOfNewLocation(location);
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
// Register the listener with the Location Manager to receive location updates
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
I changed the above code given in the linked answer as following but no success.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView txtView = (TextView) findViewById(R.id.tv1);
txtView.setText("ayyo samitha");
////
// Acquire a reference to the system Location Manager
LocationManager locationManager;
locationManager= (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
// Define a listener that responds to location updates
LocationListener locationListener = new LocationListener() {
public void onLocationChanged(Location location) {
// Called when a new location is found by the network location provider.
makeUseOfNewLocation(location);
}
private void makeUseOfNewLocation(Location location) {
txtView.setText("sam came in");
txtView.append(location.toString());
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {
// makeUseOfNewLocation(location);
}
public void onProviderDisabled(String provider) {}
};
// Register the listener with the Location Manager to receive location updates
if (locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
}
}
How to accomplish what I want by correcting above code or any other method? Note that I want to get the location name, but not the longitudes and latitudes. Can somebody please help me.
What you are referring to here (showing location name on older phones) is done using "Cell Broadcast" (or "CB"). This has absolutely nothing to do with the Location API or any variations on that.
Cell towers can send out broadcast information that can be received by devices (something like "one to many SMS"). Some operators have used Cell Broadcast to broadcast the name of the location where the cell tower is. Some operators have used Cell Broadcast to broadcast the location (lat/long) of the cell tower. Some operators have used Cell Broadcast to send advertising tickers. There are no standards for the information contained in a CB broadcast message and each mobile operator can choose to use this or not.
Since most operators do not send these messages, it probably doesn't make sense to invest any time in trying to receive and decode them. But if you want to try, you can register a BroadcastReceiver listening for this Intent action: android.provider.Telephony.SMS_CB_RECEIVED. See the documentation for more details about what data is contained in the Intent.
The problem is that the code you tried does work, probably just not as well as you wished. For example, the accuracy such a method provides on Samsung Galaxy S3 is 2000m, meaning the actual position is anywhere within a circle of 2 kilometers radius. Additional it would probably take quite a large change in location before your app would be informed of a location change since the margin of error is so big.
A GPS or LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY (if Google Play Services is used) is required to get a reasonably good location. This does require android.permission.ACCESS_FINE_LOCATION, however unless you only require km level accuracy, otherwise this permission is a must.
Finally note that using Google Play Services with LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY I can get location data as accurate as 10m without turning on GPS, so this should still satisfy your requirement.
Below is a complete example:
AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
MainActivity.java
import android.app.Activity;
import android.location.Location;
import android.os.Bundle;
import android.widget.TextView;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.FusedLocationProviderApi;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
public class MainActivity extends Activity implements
com.google.android.gms.location.LocationListener, ConnectionCallbacks,
OnConnectionFailedListener {
private final FusedLocationProviderApi fusedLocationProviderApi = LocationServices.FusedLocationApi;
private GoogleApiClient mGoogleAPIClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGoogleAPIClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API).addConnectionCallbacks(this)
.addOnConnectionFailedListener(this).build();
}
#Override
protected void onResume() {
super.onResume();
mGoogleAPIClient.connect();
}
#Override
protected void onPause() {
super.onPause();
if (mGoogleAPIClient != null) {
mGoogleAPIClient.disconnect();
}
}
#Override
public void onConnected(Bundle arg0) {
final LocationRequest locationRequest = LocationRequest.create();
locationRequest
.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
locationRequest.setInterval(30 * 1000);
locationRequest.setFastestInterval(5 * 1000);
fusedLocationProviderApi.requestLocationUpdates(mGoogleAPIClient,
locationRequest, this);
}
#Override
public void onConnectionSuspended(int arg0) {
// TODO Auto-generated method stub
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
// TODO Auto-generated method stub
}
#Override
public void onLocationChanged(Location location) {
// the location is no more than 10 min old, and with reasonable
// accurarcy (50m), done
if (System.currentTimeMillis() < location.getTime() + 10 * 60 * 1000
&& location.getAccuracy() < 50) {
mGoogleAPIClient.disconnect();
mGoogleAPIClient = null;
((TextView) findViewById(R.id.test)).setText(location.toString());
}
}
}
According to android docs using LocationManager is not the current recomended API (see reference):
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.
To learn how to set up the Google Services client library, see Setup in the Google Play services guide.
Once you have linked Google Services client library to your app you can achieve user location using FusedLocationProviderApi:
import android.location.Location;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.FusedLocationProviderApi;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
public class MainActivity extends ActionBarActivity
implements ConnectionCallbacks, OnConnectionFailedListener {
// ..
private GoogleApiClient mGoogleAPIClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// create google api client object
mGoogleAPIClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
#Override
protected void onStart() {
super.onStart();
mGoogleAPIClient.connect();
}
#Override
protected void onStop() {
super.onStop();
mGoogleAPIClient.disconnect();
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Toast.makeText(this,
"Could not connect to Google Play Services",
Toast.LENGTH_SHORT).show();
finish();
}
#Override
public void onConnected(Bundle bundle) {
Log.i(TAG,
"Successfuly connect to Google Play Services");
// retrieve last location once connected
Location lastLocation = LocationServices.FusedLocationApi
.getLastLocation(mGoogleAPIClient);
if (lastLocation == null) {
// should request new one
// location should be enabled
Log.i(TAG,
"No location data previously acquired.. should request!");
Toast.makeText(this,
"Requesting location data ..",
Toast.LENGTH_SHORT).show();
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(5000);
PendingResult<Status> result = LocationServices.FusedLocationApi
.requestLocationUpdates(mGoogleAPIClient,
locationRequest,
new LocationListener() {
#Override
public void onLocationChanged(Location location) {
makeUseOfNewLocation(location);
}
});
// TODO: use result to retrieve more info
} else {
makeUseOfNewLocation(lastLocation);
}
}
#Override
public void onConnectionSuspended(int i) {
}
private void makeUseOfNewLocation(Location location) {
// do your stuff here
}
I have tested the code above and it works without internet connection but it requires that user enable location feature on device. Also it requires that the user have already enabled Location History feature into location feature.
Hope that this helps you.
You can try getting a country level accuracy using the Locale object or using the Telephony service. No internet or GPS required.
Getting country code from Locale:
String locale = context.getResources().getConfiguration().locale.getCountry();
Getting country code from Android's Telephony service:
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
// Will work on all networks. Only provide the SIM card's country
String countryCode = tm.getSimCountryIso();
// Might not work well on CDMA networks. Will provide the country code
// for the country the device is currently in.
String currentCountryCode = tm.getNetworkCountryIso();
Better code samples and discussion here.
Good luck with this. It's called geocoder. Or more specifically reverse geocoding to turn coordinates into a human readable output. I'm fairly sure the one google provides is a pay service but you get a bunch free. So plan on caching the results and using your cached results when ever possible.
List<Address> list = geoCoder.getFromLocation(location
.getLatitude(), location.getLongitude(), 1);
if (list != null & list.size() > 0) {
Address address = list.get(0);
result = address.getLocality();
return result;
https://developer.android.com/training/location/display-address.html
How to get city name from latitude and longitude coordinates in Google Maps?
try this code..
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
public class AppLocationService extends Service implements LocationListener {
protected LocationManager locationManager;
Location location;
private static final long MIN_DISTANCE_FOR_UPDATE = 10;
private static final long MIN_TIME_FOR_UPDATE = 1000 * 60 * 2;
public AppLocationService(Context context) {
locationManager = (LocationManager) context
.getSystemService(LOCATION_SERVICE);
}
public Location getLocation(String provider) {
if (locationManager.isProviderEnabled(provider)) {
locationManager.requestLocationUpdates(provider,
MIN_TIME_FOR_UPDATE, MIN_DISTANCE_FOR_UPDATE, this);
if (locationManager != null) {
location = locationManager.getLastKnownLocation(provider);
return location;
}
}
return null;
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
and next class is
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.provider.Settings;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class AndroidLocationActivity extends Activity {
Button btnGPSShowLocation;
Button btnNWShowLocation;
AppLocationService appLocationService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
appLocationService = new AppLocationService(
AndroidLocationActivity.this);
btnGPSShowLocation = (Button) findViewById(R.id.btnGPSShowLocation);
btnGPSShowLocation.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Location gpsLocation = appLocationService
.getLocation(LocationManager.GPS_PROVIDER);
if (gpsLocation != null) {
double latitude = gpsLocation.getLatitude();
double longitude = gpsLocation.getLongitude();
Toast.makeText(
getApplicationContext(),
"Mobile Location (GPS): \nLatitude: " + latitude
+ "\nLongitude: " + longitude,
Toast.LENGTH_LONG).show();
} else {
showSettingsAlert("GPS");
}
}
});
btnNWShowLocation = (Button) findViewById(R.id.btnNWShowLocation);
btnNWShowLocation.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Location nwLocation = appLocationService
.getLocation(LocationManager.NETWORK_PROVIDER);
if (nwLocation != null) {
double latitude = nwLocation.getLatitude();
double longitude = nwLocation.getLongitude();
Toast.makeText(
getApplicationContext(),
"Mobile Location (NW): \nLatitude: " + latitude
+ "\nLongitude: " + longitude,
Toast.LENGTH_LONG).show();
} else {
showSettingsAlert("NETWORK");
}
}
});
}
public void showSettingsAlert(String provider) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(
AndroidLocationActivity.this);
alertDialog.setTitle(provider + " SETTINGS");
alertDialog
.setMessage(provider + " is not enabled! Want to go to settings menu?");
alertDialog.setPositiveButton("Settings",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(
Settings.ACTION_LOCATION_SOURCE_SETTINGS);
AndroidLocationActivity.this.startActivity(intent);
}
});
alertDialog.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
alertDialog.show();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
and this user permission given At Manifest File
<!-- to get location using GPS -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- to get location using NetworkProvider -->
<uses-permission android:name="android.permission.INTERNET" />