FusedLocationProvider broken? - android

I have a FusedLocationProviderClient that requests locations every 2 minutes. We've had the issue for ages now that in a certain circumstance users would not receive location updates and due to how we handled it in the app, we effectively locked the user out of a major section of the app.
After revisiting the topic again, I found that according to the developer guide, the cache of locations is cleared is you disable GPS.
Fine and dandy, so next time the user visits the app, you'd expect that the cache is cleared, it takes a little longer for the client to get an update and you're good to go again. Sadly, that's not the case. I disabled gps at 15:55, left it off until 16:00, reenabled it at 16:00 and still haven't gotten an update at 16:40
I figured that I might be getting an exception somewhere inside the request-task, so I added all 4 callbacks (onFailure, onCanceled, onSuccess, onComplete) and got onSuccess and onComplete signalling me that nothing's amiss.
I'm using play-services-base 16.0.1 and play-services-location 16.0.0. I have granted the permission ACCESS_FINE_LOCATION and I definitely started my request for location updates using FusedLocationProviderClient::requestLocationUpdates
here is my code
public class LocationHandler extends LocationCallback {
private static final String APPSTART_LOCATION = "APPSTART_LOCATION";
private static final String MOST_RECENT_LOCATION = "MOST_RECENT_LOCATION";
private final static int GPS_TIMEOUT = 3000;
private final Context context;
private final PreferencesHandler preferences;
private final Bus eventBus;
private BehaviorRelay<Location> subject;
private FusedLocationProviderClient client;
private boolean isRequestingLocations = false;
private Disposable timerDisposable;
public LocationHandler(Context context, PreferencesHandler preferences, Bus eventBus) {
this.context = context;
this.preferences = preferences;
this.eventBus = eventBus;
subject = BehaviorRelay.create();
client = new FusedLocationProviderClient(context);
}
#SuppressLint("MissingPermission")
public void connect() {
if (!isRequestingLocations && hasRequiredPermissions()) {
client.requestLocationUpdates(createLocationRequest(), this, null)
.addOnFailureListener(e -> Timber.e(e, "onFailure"))
.addOnCanceledListener(() -> Timber.d("onCanceled"))
.addOnSuccessListener(aVoid -> Timber.d("onSuccess"))
.addOnCompleteListener(task -> Timber.d("onComplete"));
isRequestingLocations = true;
//start a timer so the client will act like nothing's amiss if he didn't receive a location-update within the given timeout
timerDisposable = Observable.timer(GPS_TIMEOUT, TimeUnit.MILLISECONDS).subscribe(ignored -> {
//the reason why this happens is because the provider just uses cached locations. When the user deactivates GPS, the cache is cleared
//once you reactivate gps it should technically work, but it takes a long time nonetheless
ReportingUtil.trackFbEvent(new GPS_NO_LOCATION_RECEIVED((int) TimeUnit.MILLISECONDS.toSeconds(GPS_TIMEOUT)));
preferences.setHasGeolocation(true);
eventBus.post(new NoLocationReceivedEvent());
eventBus.post(new FirstLocationArrivedEvent());
});
}
}
private LocationRequest createLocationRequest() {
LocationRequest request = new LocationRequest();
long interval = TimeUnit.MINUTES.toMillis(Constants.GPSINTERVAL);
request.setInterval(interval);
request.setFastestInterval(interval);
request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
return request;
}
public void disconnect() {
if (isRequestingLocations) {
timerDisposable.dispose();
client.removeLocationUpdates(this);
isRequestingLocations = false;
}
}
public Observable<Location> getAllLocations() {
return subject;
}
#Override
public void onLocationResult(LocationResult result) {
Timber.d("onLocationResult: %s", result);
if (result != null && result.getLocations().size() != 0) {
Location location = result.getLocations().get(0);
if (location != null) {
if (timerDisposable != null) {
timerDisposable.dispose();
}
subject.accept(new Location(location));
}
}
}
private boolean hasRequiredPermissions() {
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
public void restart() {
disconnect();
connect();
}
}

Related

Location is empty at the start

I am reframing my last question, which is unanswered, and I have rewritten the problem following Google's BasicLocation.
My main activity is defined as:
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
// private LocationCallback locationCallback;
// private FusedLocationProviderClient mFusedLocationClient;
private FusedLocationProviderClient mFusedLocationClient;
protected Location mLastLocation;
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
private static final String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawer_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
navigationView.setNavigationItemSelectedListener(this);
ImageButton leftNav = findViewById(R.id.left_nav);
ImageButton rightNav = findViewById(R.id.right_nav);
leftNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
if (tab > 0) {
tab--;
viewPager.setCurrentItem(tab);
} else if (tab == 0) {
viewPager.setCurrentItem(tab);
}
}
});
rightNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
tab++;
viewPager.setCurrentItem(tab);
}
});
}
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
with latlang.[Lat,Lang] is in a seperate file:
public class latlang {
public static double Lat;
public static double Lang;
}
and the location file, which is the first fragment in the viewpager is defined as:
public class SunFragment extends Fragment {
List<SunSession> sunsList;
Typeface sunfont;
//to be called by the MainActivity
public SunFragment() {
// Required empty public constructor
}
// Keys for storing activity state.
// private static final String KEY_CAMERA_POSITION = "camera_position";
private static final String KEY_LOCATION_NAME = "location_name";
public String location;//="No location name found";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retrieve location and camera position from saved instance state.
if (savedInstanceState != null) {
location = savedInstanceState.getCharSequence(KEY_LOCATION_NAME).toString();
System.out.println("OnCreate location "+location);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_sun, container, false);
onSaveInstanceState(new Bundle());
//SecondFragment secondFragment = new SecondFragment();
//secondFragment.getDeviceLocation();
RecyclerView rv = rootView.findViewById(R.id.rv_recycler_view);
rv.setNestedScrollingEnabled(false);
rv.setHasFixedSize(true);
//MyAdapter adapter = new MyAdapter(new String[]{"Today", "Golden Hour", "Blue Hour", "Civil Twilight", "Nautical Twilight", "Astronomical Twilight", "Hello", "World"});
//rv.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(llm);
System.out.println("location "+location);
/*
Reversegeocoding location
*/
String location="No location name found";
String errorMessage = "";
List<Address> addresses = null;
Geocoder geocoder = new Geocoder(getContext(), Locale.getDefault());
try {
addresses = geocoder.getFromLocation(
latlang.Lat,
latlang.Lang,
1);
} catch (IOException ioException) {
// Catch network or other I/O problems.
errorMessage = getString(R.string.service_not_available);
// Log.e(TAG, errorMessage, ioException);
if (getView() != null){
Snackbar.make(getView(), errorMessage, Snackbar.LENGTH_LONG).show();
}
} catch (IllegalArgumentException illegalArgumentException) {
// Catch invalid latitude or longitude values.
errorMessage = getString(R.string.invalid_lat_long_used);
if (getView() != null){
Snackbar.make(getView(),
"Illegal Latitude = " + latlang.Lat + ", Longitude = " +
latlang.Lang, Snackbar.LENGTH_LONG).show();
}
}
if (addresses == null || addresses.size() == 0) {
if (errorMessage.isEmpty()) {
System.out.println("Adress Empty No Address Found");// Snackbar.LENGTH_LONG).show();
location = "Lat:"+latlang.Lat+" Lang: "+latlang.Lang;
}
} else {
location = addresses.get(0).getAddressLine(0);//+", "+ addresses.get(0).getLocality();
/* for(int i = 0; i <= addresses.get(0).getMaxAddressLineIndex(); i++) {
location = addresses.get(0).getAddressLine(i);
}*/
}
The problem with this is evident from the logcat:
I/System.out: location null
I/Google Maps Android API: Google Play services package version: 17785022
I/Choreographer: Skipped 31 frames! The application may be doing too much work on its main thread.
I/System.out: Position:0
I/System.out: Position:1
I/System.out: Position:2
I/zygote: Do full code cache collection, code=202KB, data=177KB
I/zygote: After code cache collection, code=129KB, data=91KB
I/zygote: JIT allocated 56KB for compiled code of void android.view.View.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/zygote: Background concurrent copying GC freed 44415(2MB) AllocSpace objects, 7(136KB) LOS objects, 49% free, 3MB/6MB, paused 294us total 102.458ms
I/System.out: Position:3
I/System.out: Position:4
I/zygote: Do partial code cache collection, code=193KB, data=126KB
I/zygote: After code cache collection, code=193KB, data=126KB
I/zygote: Increasing code cache capacity to 1024KB
I/zygote: JIT allocated 71KB for compiled code of void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/zygote: Compiler allocated 4MB to compile void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
E/MainActivity: Latit: 37.42342342342342
This shows, at the start, location is null,
I/System.out: location null
then the recyclerview of the sunfragment is created
I/System.out: Position:0
I/System.out: Position:1
I/System.out: Position:2
and after that I am getting the location:
E/MainActivity: Latit: 37.42342342342342
Link of the complete code:https://drive.google.com/file/d/1pMl_3Lf76sy82C0J4b-9ta4jbSHonJ2y/view?usp=sharing
Is it somehow possible to get the location first before creating the sunfragment's oncreateview?
I found something wrong about your code (I may be wrong):
Why fields of latlang are static? It doesn't looks like they should.
At SunFragment.onCreate() you are reading location if savedInstanceState != null. savedInstanceState is not null only if activity that holds this fragment was restored from saved state. It may not happen at all.
You should use fragment's arguments (Bundle) to pass initial data to fragment
You should implement Parcelable interface for latlang to be able to pass custom class thru Bundle
I think that's not everything but for me it seems like enough for this code to not work as you expected
As stated by the previous answer there are a lot of issues in your code.
Apart from that understand that last known location may not always return a value. Gps basically has two data types: Ephemeris (precise nav data) and Almanac (coarse data). Now when the receiver is cold started ie after gps has been off for more than 8-10 mins, there is basically no last known location (the duration may vary based on the device but the basic idea is this).
So when you do not get last known location, fetch the actual live location using the fused client. Also since you are saving the data in shared preference and fetching it in your fragment, i believe your fragment is going to heavily rely on this data. So i would suggest either of the following two approaches to get correct result
1) do not fetch the location in the nesting activity at all. Just do it in the fragment where it is needed. This will not work if other fragments in your viewpager also need the location.
2) If you must have the location in your activity and it is a dependency in the container fragments, you can use two approaches here as well. My approaches rely on event bus.. Event bus, otto or rxbus anything will do
2a) do not add anything to the viewpager. basically fetch the location first fully and then add stuff to the viewpager once you get the location callback.
2b) Add stuff to the viewpager from the start. In the activity once you get the location, use the event bus to inform the fragments of the same and on getting the event in the fragments, actually start what you need to do.
I have previously used both these approaches and everything works. Now it is entirely up to your use case to use what suits you. Either ways it is too long a code and too complicated to post everything here.
Right now you are using like this:
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
In your else statement check if getLastLocation() is not null. if not null, replace the fragment.
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
if(getLastLocation() != null){
//replace your fragment
}else{//Null Location}
}
}
Use a service class to handle location service precisely. Here, i'm giving you a custom LocationService class which i used in many projects to collect the location data from background continuously
LocationService.kt
import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
class LocationService : Service() {
//Custom Location Binder class
inner class LocationBinder : Binder() {
val locationService: LocationService
get() = this#LocationService
}
companion object {
val TAG = "LocationService_123"
private val UPDATE_INTERVAL = (4 * 1000).toLong() /* 4 secs */
private val FASTEST_INTERVAL: Long = 2000 /* 2 sec */
private var INSTANCE: LocationService? = null
fun isInstanceCreated(): Boolean {
return (INSTANCE != null)
}
}
private var mFusedLocationClient: FusedLocationProviderClient? = null
private var mLocationListener: LocationListener? = null
private var mLocationBinder = LocationBinder()
fun setLocationListener(locationListener: LocationListener) {
this.mLocationListener = locationListener
}
override fun onBind(intent: Intent): IBinder? {
return mLocationBinder
}
override fun onCreate() {
INSTANCE = this
super.onCreate()
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
if (Build.VERSION.SDK_INT >= 26) {
val CHANNEL_ID = "ostad_gps"
val channel = NotificationChannel(
CHANNEL_ID,
"Ostad GPS",
NotificationManager.IMPORTANCE_DEFAULT)
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("").build()
startForeground(1, notification)
}
}
override fun onDestroy() {
INSTANCE = null
super.onDestroy()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand: called.")
getLocation()
return Service.START_NOT_STICKY
}
private fun getLocation() {
// ---------------------------------- LocationRequest ------------------------------------
// Create the location request to start receiving updates
val mLocationRequestHighAccuracy = LocationRequest()
mLocationRequestHighAccuracy.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
mLocationRequestHighAccuracy.interval = UPDATE_INTERVAL
mLocationRequestHighAccuracy.fastestInterval = FASTEST_INTERVAL
// new Google API SDK v11 uses getFusedLocationProviderClient(this)
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
Log.d(TAG, "getLocation: stopping the location mitchService.")
stopSelf()
return
}
Log.d(TAG, "getLocation: getting location information.")
mFusedLocationClient!!.requestLocationUpdates(
mLocationRequestHighAccuracy, object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
val location = locationResult!!.lastLocation
Log.d(TAG, "onLocationResult: got location result: $location")
if (location != null) {
if (mLocationListener != null)
mLocationListener!!.onLocationChanged(location)
}
}
},
Looper.myLooper()
) // Looper.myLooper tells this to repeat forever until thread is destroyed
}
}
add this in your AndroidManifest file and start the LocationService from you MainActivity.
By looking at the code, it seems you do not need a very accurate location, you will be fine with last known location. This value might be null in some cases, like you have already experienced. Simple answer to your question is no, you cannot get not null location before creating SunFragment. Following steps is to load location in background and update UI once found.
Request last known location in MainActivity
Keep a reference of last location in cache for easy loading and better user
experience
If last location is null, request location updates until you get a good fix
Have a listener in SunFragment to track location updates
Here are some code you need (Please do read them)
Use the LocationUtil to handle location related events (I prefer LocationManager over FusedLocationProviderClient);
public class LocationUtil {
public static void updateLastKnownLocation(Context context) {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if(hasSelfPermission(context, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION})) {
try {
Location currentBestLocation;
Location gpsLocation = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
Location lbsLocation = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if(isBetterLocation(lbsLocation, gpsLocation)) {
currentBestLocation = lbsLocation;
} else {
currentBestLocation = gpsLocation;
}
if(currentBestLocation == null) {
requestLocationUpdates(lm);
} else {
updateCacheLocation(currentBestLocation);
}
} catch (SecurityException se) {
// unlikely as permission checks
se.printStackTrace();
} catch (Exception e) {
// unexpected
e.printStackTrace();
}
}
}
private static void updateCacheLocation(Location location) {
if(location == null) return;
LocationLite temp = new LocationLite();
temp.lat = location.getLatitude();
temp.lon = location.getLongitude();
Gson gson = new Gson();
String locationString = gson.toJson(temp);
AppCache.setLastLocation(locationString);
}
#SuppressLint("MissingPermission")
private static void requestLocationUpdates(LocationManager lm) {
try {
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0.0F, new LocationListener() {
#Override
public void onLocationChanged(Location location) {
updateCacheLocation(location);
lm.removeUpdates(this);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
// doing nothing
}
#Override
public void onProviderEnabled(String s) {
// doing nothing
}
#Override
public void onProviderDisabled(String s) {
// doing nothing
}
});
}catch (Exception e) {
e.printStackTrace();
}
}
private static boolean isBetterLocation(Location location, Location currentBestLocation) {
int TWO_MINUTES = 1000 * 60 * 2;
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
if (location == null) {
// A new location is always better than no location
return false;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(), currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
private static boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
public static boolean hasSelfPermission(Context context, String[] permissions) {
// Below Android M all permissions are granted at install time and are already available.
if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)) {
return true;
}
// Verify that all required permissions have been granted
for (String permission : permissions) {
if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
}
Use AppCache to store last location;
public class AppCache {
public static final String KEY_LAST_LOCATION = "_key_last_location";
private static SharedPreferences mPreference;
static {
mPreference = PreferenceManager.getDefaultSharedPreferences(App.getApp().getApplicationContext());
}
public static String getLastLocation() {
return mPreference.getString(KEY_LAST_LOCATION, null);
}
public static String getLastLocation(String defaultValue) {
return mPreference.getString(KEY_LAST_LOCATION, defaultValue);
}
public static void setLastLocation(String lastLocation) {
mPreference.edit().putString(KEY_LAST_LOCATION, lastLocation).commit();
}
public static void registerPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
mPreference.registerOnSharedPreferenceChangeListener(listener);
}
public static void unregisterPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
mPreference.unregisterOnSharedPreferenceChangeListener(listener);
}
}
Put the following code into your MainActivity onCreate() This will call locationManager to get last known location and update app cache.
LocationUtil.updateLastKnownLocation(MainActivity.this);
Also replace fetchLocation(); in onRequestPermissionsResult method with above line of code, so it will look like;
#Override
public void onRequestPermissionsResult(...){
switch (requestCode) {
case 101:
{
...
// permission was granted
//fetchLocation();
LocationUtil.updateLastKnownLocation(MainActivity.this);
} else {
// Show some error
}
return;
}
}
}
I did not use your latlang class. (Please make sure all class names follow Java coding standards) Instead use LocationLite to store location in cache. Also I used GSON google library to convert and restore pojo to JSON and backward.
public class LocationLite {
public double lat;
public double lon;
public String address;
}
Final changes in SunFragment.
Make SunAdapter as a member variable, and SharedPreferences.OnSharedPreferenceChangeListener to listen to any changes on location value.
SunAdapter mAdapter;
SharedPreferences.OnSharedPreferenceChangeListener mPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if(AppCache.KEY_LAST_LOCATION.equalsIgnoreCase(key)) {
// location value has change, update data-set
SunSession sunSession = sunsList.get(0);
sunSession.setId(sharedPreferences.getString(key, "No Location"));
sunsList.add(0, sunSession);
mAdapter.notifyDataSetChanged();
}
}
};
Start listening to preference changes in onStart() and unregister in onStop()
#Override
public void onStart() {
super.onStart();
AppCache.registerPreferenceChangeListener(mPreferenceChangeListener);
}
#Override
public void onStop() {
super.onStop();
AppCache.unregisterPreferenceChangeListener(mPreferenceChangeListener);
}
Finally when populating first SunSession use the following instead location local variable. So It will look like following;
sunsList.add(
new SunSession(
AppCache.getLastLocation("Searching location..."),
"",
sun_rise,
"",
sun_set,
"&#xf0c9",
moon_rise,
"&#xf0ca",
moon_set));
That's all. Feel free to ask anything you do not understand.

location is always null at startup [duplicate]

I am reframing my last question, which is unanswered, and I have rewritten the problem following Google's BasicLocation.
My main activity is defined as:
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
// private LocationCallback locationCallback;
// private FusedLocationProviderClient mFusedLocationClient;
private FusedLocationProviderClient mFusedLocationClient;
protected Location mLastLocation;
private static final int REQUEST_PERMISSIONS_REQUEST_CODE = 34;
private static final String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawer_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
navigationView.setNavigationItemSelectedListener(this);
ImageButton leftNav = findViewById(R.id.left_nav);
ImageButton rightNav = findViewById(R.id.right_nav);
leftNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
if (tab > 0) {
tab--;
viewPager.setCurrentItem(tab);
} else if (tab == 0) {
viewPager.setCurrentItem(tab);
}
}
});
rightNav.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int tab = viewPager.getCurrentItem();
tab++;
viewPager.setCurrentItem(tab);
}
});
}
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
with latlang.[Lat,Lang] is in a seperate file:
public class latlang {
public static double Lat;
public static double Lang;
}
and the location file, which is the first fragment in the viewpager is defined as:
public class SunFragment extends Fragment {
List<SunSession> sunsList;
Typeface sunfont;
//to be called by the MainActivity
public SunFragment() {
// Required empty public constructor
}
// Keys for storing activity state.
// private static final String KEY_CAMERA_POSITION = "camera_position";
private static final String KEY_LOCATION_NAME = "location_name";
public String location;//="No location name found";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retrieve location and camera position from saved instance state.
if (savedInstanceState != null) {
location = savedInstanceState.getCharSequence(KEY_LOCATION_NAME).toString();
System.out.println("OnCreate location "+location);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_sun, container, false);
onSaveInstanceState(new Bundle());
//SecondFragment secondFragment = new SecondFragment();
//secondFragment.getDeviceLocation();
RecyclerView rv = rootView.findViewById(R.id.rv_recycler_view);
rv.setNestedScrollingEnabled(false);
rv.setHasFixedSize(true);
//MyAdapter adapter = new MyAdapter(new String[]{"Today", "Golden Hour", "Blue Hour", "Civil Twilight", "Nautical Twilight", "Astronomical Twilight", "Hello", "World"});
//rv.setAdapter(adapter);
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(llm);
System.out.println("location "+location);
/*
Reversegeocoding location
*/
String location="No location name found";
String errorMessage = "";
List<Address> addresses = null;
Geocoder geocoder = new Geocoder(getContext(), Locale.getDefault());
try {
addresses = geocoder.getFromLocation(
latlang.Lat,
latlang.Lang,
1);
} catch (IOException ioException) {
// Catch network or other I/O problems.
errorMessage = getString(R.string.service_not_available);
// Log.e(TAG, errorMessage, ioException);
if (getView() != null){
Snackbar.make(getView(), errorMessage, Snackbar.LENGTH_LONG).show();
}
} catch (IllegalArgumentException illegalArgumentException) {
// Catch invalid latitude or longitude values.
errorMessage = getString(R.string.invalid_lat_long_used);
if (getView() != null){
Snackbar.make(getView(),
"Illegal Latitude = " + latlang.Lat + ", Longitude = " +
latlang.Lang, Snackbar.LENGTH_LONG).show();
}
}
if (addresses == null || addresses.size() == 0) {
if (errorMessage.isEmpty()) {
System.out.println("Adress Empty No Address Found");// Snackbar.LENGTH_LONG).show();
location = "Lat:"+latlang.Lat+" Lang: "+latlang.Lang;
}
} else {
location = addresses.get(0).getAddressLine(0);//+", "+ addresses.get(0).getLocality();
/* for(int i = 0; i <= addresses.get(0).getMaxAddressLineIndex(); i++) {
location = addresses.get(0).getAddressLine(i);
}*/
}
The problem with this is evident from the logcat:
I/System.out: location null
I/Google Maps Android API: Google Play services package version: 17785022
I/Choreographer: Skipped 31 frames! The application may be doing too much work on its main thread.
I/System.out: Position:0
I/System.out: Position:1
I/System.out: Position:2
I/zygote: Do full code cache collection, code=202KB, data=177KB
I/zygote: After code cache collection, code=129KB, data=91KB
I/zygote: JIT allocated 56KB for compiled code of void android.view.View.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/zygote: Background concurrent copying GC freed 44415(2MB) AllocSpace objects, 7(136KB) LOS objects, 49% free, 3MB/6MB, paused 294us total 102.458ms
I/System.out: Position:3
I/System.out: Position:4
I/zygote: Do partial code cache collection, code=193KB, data=126KB
I/zygote: After code cache collection, code=193KB, data=126KB
I/zygote: Increasing code cache capacity to 1024KB
I/zygote: JIT allocated 71KB for compiled code of void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/zygote: Compiler allocated 4MB to compile void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
E/MainActivity: Latit: 37.42342342342342
This shows, at the start, location is null,
I/System.out: location null
then the recyclerview of the sunfragment is created
I/System.out: Position:0
I/System.out: Position:1
I/System.out: Position:2
and after that I am getting the location:
E/MainActivity: Latit: 37.42342342342342
Link of the complete code:https://drive.google.com/file/d/1pMl_3Lf76sy82C0J4b-9ta4jbSHonJ2y/view?usp=sharing
Is it somehow possible to get the location first before creating the sunfragment's oncreateview?
I found something wrong about your code (I may be wrong):
Why fields of latlang are static? It doesn't looks like they should.
At SunFragment.onCreate() you are reading location if savedInstanceState != null. savedInstanceState is not null only if activity that holds this fragment was restored from saved state. It may not happen at all.
You should use fragment's arguments (Bundle) to pass initial data to fragment
You should implement Parcelable interface for latlang to be able to pass custom class thru Bundle
I think that's not everything but for me it seems like enough for this code to not work as you expected
As stated by the previous answer there are a lot of issues in your code.
Apart from that understand that last known location may not always return a value. Gps basically has two data types: Ephemeris (precise nav data) and Almanac (coarse data). Now when the receiver is cold started ie after gps has been off for more than 8-10 mins, there is basically no last known location (the duration may vary based on the device but the basic idea is this).
So when you do not get last known location, fetch the actual live location using the fused client. Also since you are saving the data in shared preference and fetching it in your fragment, i believe your fragment is going to heavily rely on this data. So i would suggest either of the following two approaches to get correct result
1) do not fetch the location in the nesting activity at all. Just do it in the fragment where it is needed. This will not work if other fragments in your viewpager also need the location.
2) If you must have the location in your activity and it is a dependency in the container fragments, you can use two approaches here as well. My approaches rely on event bus.. Event bus, otto or rxbus anything will do
2a) do not add anything to the viewpager. basically fetch the location first fully and then add stuff to the viewpager once you get the location callback.
2b) Add stuff to the viewpager from the start. In the activity once you get the location, use the event bus to inform the fragments of the same and on getting the event in the fragments, actually start what you need to do.
I have previously used both these approaches and everything works. Now it is entirely up to your use case to use what suits you. Either ways it is too long a code and too complicated to post everything here.
Right now you are using like this:
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
getLastLocation();
}
}
In your else statement check if getLastLocation() is not null. if not null, replace the fragment.
#Override
public void onStart() {
super.onStart();
if (!checkPermissions()) {
requestPermissions();
} else {
if(getLastLocation() != null){
//replace your fragment
}else{//Null Location}
}
}
Use a service class to handle location service precisely. Here, i'm giving you a custom LocationService class which i used in many projects to collect the location data from background continuously
LocationService.kt
import android.Manifest
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.os.Looper
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
class LocationService : Service() {
//Custom Location Binder class
inner class LocationBinder : Binder() {
val locationService: LocationService
get() = this#LocationService
}
companion object {
val TAG = "LocationService_123"
private val UPDATE_INTERVAL = (4 * 1000).toLong() /* 4 secs */
private val FASTEST_INTERVAL: Long = 2000 /* 2 sec */
private var INSTANCE: LocationService? = null
fun isInstanceCreated(): Boolean {
return (INSTANCE != null)
}
}
private var mFusedLocationClient: FusedLocationProviderClient? = null
private var mLocationListener: LocationListener? = null
private var mLocationBinder = LocationBinder()
fun setLocationListener(locationListener: LocationListener) {
this.mLocationListener = locationListener
}
override fun onBind(intent: Intent): IBinder? {
return mLocationBinder
}
override fun onCreate() {
INSTANCE = this
super.onCreate()
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
if (Build.VERSION.SDK_INT >= 26) {
val CHANNEL_ID = "ostad_gps"
val channel = NotificationChannel(
CHANNEL_ID,
"Ostad GPS",
NotificationManager.IMPORTANCE_DEFAULT)
(getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("")
.setContentText("").build()
startForeground(1, notification)
}
}
override fun onDestroy() {
INSTANCE = null
super.onDestroy()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand: called.")
getLocation()
return Service.START_NOT_STICKY
}
private fun getLocation() {
// ---------------------------------- LocationRequest ------------------------------------
// Create the location request to start receiving updates
val mLocationRequestHighAccuracy = LocationRequest()
mLocationRequestHighAccuracy.priority = LocationRequest.PRIORITY_HIGH_ACCURACY
mLocationRequestHighAccuracy.interval = UPDATE_INTERVAL
mLocationRequestHighAccuracy.fastestInterval = FASTEST_INTERVAL
// new Google API SDK v11 uses getFusedLocationProviderClient(this)
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) != PackageManager.PERMISSION_GRANTED
) {
Log.d(TAG, "getLocation: stopping the location mitchService.")
stopSelf()
return
}
Log.d(TAG, "getLocation: getting location information.")
mFusedLocationClient!!.requestLocationUpdates(
mLocationRequestHighAccuracy, object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
val location = locationResult!!.lastLocation
Log.d(TAG, "onLocationResult: got location result: $location")
if (location != null) {
if (mLocationListener != null)
mLocationListener!!.onLocationChanged(location)
}
}
},
Looper.myLooper()
) // Looper.myLooper tells this to repeat forever until thread is destroyed
}
}
add this in your AndroidManifest file and start the LocationService from you MainActivity.
By looking at the code, it seems you do not need a very accurate location, you will be fine with last known location. This value might be null in some cases, like you have already experienced. Simple answer to your question is no, you cannot get not null location before creating SunFragment. Following steps is to load location in background and update UI once found.
Request last known location in MainActivity
Keep a reference of last location in cache for easy loading and better user
experience
If last location is null, request location updates until you get a good fix
Have a listener in SunFragment to track location updates
Here are some code you need (Please do read them)
Use the LocationUtil to handle location related events (I prefer LocationManager over FusedLocationProviderClient);
public class LocationUtil {
public static void updateLastKnownLocation(Context context) {
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if(hasSelfPermission(context, new String[]{
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION})) {
try {
Location currentBestLocation;
Location gpsLocation = lm.getLastKnownLocation(LocationManager.GPS_PROVIDER);
Location lbsLocation = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if(isBetterLocation(lbsLocation, gpsLocation)) {
currentBestLocation = lbsLocation;
} else {
currentBestLocation = gpsLocation;
}
if(currentBestLocation == null) {
requestLocationUpdates(lm);
} else {
updateCacheLocation(currentBestLocation);
}
} catch (SecurityException se) {
// unlikely as permission checks
se.printStackTrace();
} catch (Exception e) {
// unexpected
e.printStackTrace();
}
}
}
private static void updateCacheLocation(Location location) {
if(location == null) return;
LocationLite temp = new LocationLite();
temp.lat = location.getLatitude();
temp.lon = location.getLongitude();
Gson gson = new Gson();
String locationString = gson.toJson(temp);
AppCache.setLastLocation(locationString);
}
#SuppressLint("MissingPermission")
private static void requestLocationUpdates(LocationManager lm) {
try {
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0.0F, new LocationListener() {
#Override
public void onLocationChanged(Location location) {
updateCacheLocation(location);
lm.removeUpdates(this);
}
#Override
public void onStatusChanged(String s, int i, Bundle bundle) {
// doing nothing
}
#Override
public void onProviderEnabled(String s) {
// doing nothing
}
#Override
public void onProviderDisabled(String s) {
// doing nothing
}
});
}catch (Exception e) {
e.printStackTrace();
}
}
private static boolean isBetterLocation(Location location, Location currentBestLocation) {
int TWO_MINUTES = 1000 * 60 * 2;
if (currentBestLocation == null) {
// A new location is always better than no location
return true;
}
if (location == null) {
// A new location is always better than no location
return false;
}
// Check whether the new location fix is newer or older
long timeDelta = location.getTime() - currentBestLocation.getTime();
boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
boolean isNewer = timeDelta > 0;
// If it's been more than two minutes since the current location, use the new location
// because the user has likely moved
if (isSignificantlyNewer) {
return true;
// If the new location is more than two minutes older, it must be worse
} else if (isSignificantlyOlder) {
return false;
}
// Check whether the new location fix is more or less accurate
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
boolean isLessAccurate = accuracyDelta > 0;
boolean isMoreAccurate = accuracyDelta < 0;
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
// Check if the old and new location are from the same provider
boolean isFromSameProvider = isSameProvider(location.getProvider(), currentBestLocation.getProvider());
// Determine location quality using a combination of timeliness and accuracy
if (isMoreAccurate) {
return true;
} else if (isNewer && !isLessAccurate) {
return true;
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
return true;
}
return false;
}
private static boolean isSameProvider(String provider1, String provider2) {
if (provider1 == null) {
return provider2 == null;
}
return provider1.equals(provider2);
}
public static boolean hasSelfPermission(Context context, String[] permissions) {
// Below Android M all permissions are granted at install time and are already available.
if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)) {
return true;
}
// Verify that all required permissions have been granted
for (String permission : permissions) {
if (context.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
}
Use AppCache to store last location;
public class AppCache {
public static final String KEY_LAST_LOCATION = "_key_last_location";
private static SharedPreferences mPreference;
static {
mPreference = PreferenceManager.getDefaultSharedPreferences(App.getApp().getApplicationContext());
}
public static String getLastLocation() {
return mPreference.getString(KEY_LAST_LOCATION, null);
}
public static String getLastLocation(String defaultValue) {
return mPreference.getString(KEY_LAST_LOCATION, defaultValue);
}
public static void setLastLocation(String lastLocation) {
mPreference.edit().putString(KEY_LAST_LOCATION, lastLocation).commit();
}
public static void registerPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
mPreference.registerOnSharedPreferenceChangeListener(listener);
}
public static void unregisterPreferenceChangeListener(SharedPreferences.OnSharedPreferenceChangeListener listener) {
mPreference.unregisterOnSharedPreferenceChangeListener(listener);
}
}
Put the following code into your MainActivity onCreate() This will call locationManager to get last known location and update app cache.
LocationUtil.updateLastKnownLocation(MainActivity.this);
Also replace fetchLocation(); in onRequestPermissionsResult method with above line of code, so it will look like;
#Override
public void onRequestPermissionsResult(...){
switch (requestCode) {
case 101:
{
...
// permission was granted
//fetchLocation();
LocationUtil.updateLastKnownLocation(MainActivity.this);
} else {
// Show some error
}
return;
}
}
}
I did not use your latlang class. (Please make sure all class names follow Java coding standards) Instead use LocationLite to store location in cache. Also I used GSON google library to convert and restore pojo to JSON and backward.
public class LocationLite {
public double lat;
public double lon;
public String address;
}
Final changes in SunFragment.
Make SunAdapter as a member variable, and SharedPreferences.OnSharedPreferenceChangeListener to listen to any changes on location value.
SunAdapter mAdapter;
SharedPreferences.OnSharedPreferenceChangeListener mPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if(AppCache.KEY_LAST_LOCATION.equalsIgnoreCase(key)) {
// location value has change, update data-set
SunSession sunSession = sunsList.get(0);
sunSession.setId(sharedPreferences.getString(key, "No Location"));
sunsList.add(0, sunSession);
mAdapter.notifyDataSetChanged();
}
}
};
Start listening to preference changes in onStart() and unregister in onStop()
#Override
public void onStart() {
super.onStart();
AppCache.registerPreferenceChangeListener(mPreferenceChangeListener);
}
#Override
public void onStop() {
super.onStop();
AppCache.unregisterPreferenceChangeListener(mPreferenceChangeListener);
}
Finally when populating first SunSession use the following instead location local variable. So It will look like following;
sunsList.add(
new SunSession(
AppCache.getLastLocation("Searching location..."),
"",
sun_rise,
"",
sun_set,
"&#xf0c9",
moon_rise,
"&#xf0ca",
moon_set));
That's all. Feel free to ask anything you do not understand.

Android Background Location Updates - Different Number of LatLongs in Kitkat And Marshmallow Devices

I am developing an app that gets current latlongs of a device and stores them in a file in a new line. I ran my app on kit kat version 4.4.4 and on marshmallow version 6.0.1. I took both device in my car for testing and took a long ride around the city. I noticed a weird output, the file generated from kit kat had 576 lines means that 576 pair of latlongs were recorded. But in the file generated on marshmallow device had only 250 lines of latlongs. As long as I know, according to the definition of Doze mode introduced in marshmallow, the phone only enters doze mode when the device is stationary. But in my case the device was not stationary because it was in a moving car. I am confused. Can anyone help.
Below is my Background Service Class without file saving code.
public class GPSTrackerService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(getApplicationContext());
mSettingsClient = LocationServices.getSettingsClient(getApplicationContext());
createLocationCallback();
createLocationRequest();
buildLocationSettingsRequest();
new CountDownTimer(9900000,3000){
#Override
public void onTick(long l) {
Log.d("BOSS_DK","On Tick");
}
#Override
public void onFinish() {
}
}.start();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#TargetApi(Build.VERSION_CODES.M)
public void test(){
Intent intent = new Intent();
String packageName = getPackageName();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
if (pm.isIgnoringBatteryOptimizations(packageName))
intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
else {
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
}
startActivity(intent);
}
private LocationRequest mLocationRequest;
private LocationSettingsRequest mLocationSettingsRequest;
private FusedLocationProviderClient mFusedLocationClient;
private LocationCallback mLocationCallback;
private SettingsClient mSettingsClient;
// Start Fused Location services
protected void createLocationRequest() {
// create the locatioon request and set parameters
mLocationRequest = LocationRequest.create();
mLocationRequest.setInterval(2000); // will return location after every 5 seconds
mLocationRequest.setFastestInterval(1000); // fastest rate app can handle updates
//mLocationRequest.setSmallestDisplacement(5);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
private void buildLocationSettingsRequest() {
// get current locations settings of user's device
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest);
builder.addLocationRequest(mLocationRequest);
mLocationSettingsRequest = builder.build();
startLocationUpdates();
}
private void startLocationUpdates() {
// if settings are satisfied initialize location requests
mSettingsClient.checkLocationSettings(mLocationSettingsRequest).addOnSuccessListener(new OnSuccessListener<LocationSettingsResponse>() {
#Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
//locUpdates = true;
// All location settings are satisfied.
//noinspection MissingPermission - this comment needs to stay here to stop inspection on next line
mFusedLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper());
}
})
// if settings need to be changed prompt user
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
e.printStackTrace();
}
});
}
// stop location updates
private void stopLocationUpdates() {
//locUpdates = false;
mFusedLocationClient.removeLocationUpdates(mLocationCallback).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
}
});
}
private void createLocationCallback() {
mLocationCallback = new LocationCallback() {
#Override
public void onLocationResult(LocationResult locationResult) {
super.onLocationResult(locationResult);
List<Location> locations = locationResult.getLocations();
/*if(locationResult.getLastLocation().hasAccuracy()) {
setLatLong(locationResult.getLastLocation());
}*/
for(Location location : locations){
setLatLong(location);
}
}
};
}
// Set new latitude and longitude based on location results
public void setLatLong(Location location) {
Log.d("BOSS_DK",String.valueOf(location.getLatitude())+","+String.valueOf(location.getLongitude()));
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d("BOSS_DK","service destroyed");
}
}
I guess this is due to Background Location Limitation. States:
Location retrieval behaviour is particularly important to keep in mind if your app relies on real-time alerts or motion detection while running in the background.
The system distinguishes between foreground and background apps. An app is considered to be in the foreground if any of the following is true:
It has a visible activity, whether the activity is started or paused.
It has a foreground service.
Another foreground app is connected to the app, either by binding to one of its services or by making use of one of its content providers. For example, if a foreground app binds to any of the following components within another app, that other app is considered to be in the foreground:
Input method editor (IME)
Wallpaper service
Notification listener
Voice or text service
If none of those conditions is true, the app is considered to be in the background.

Android, get the location when the screen is off

I use a started service with the fused api, and implement the location listener directly on it.
The Location keeps updating even when the screen is locked, But it stops if the screen goes off.
So, is there any way to make sure that the location will keep updating when the screen is off?
I read a lot of other questions and I don't really know what i'm missing.
public class CLocationService extends Service implements GoogleApiClient.ConnectionCallbacks, LocationListener,
GoogleApiClient.OnConnectionFailedListener {
private GoogleApiClient mGoogleApiClient;
private PowerManager.WakeLock mWakeLock;
private LocationRequest mLocationRequest;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private Boolean servicesAvailable = false;
private boolean isStarted;
public static final int LOCATION_SERVICE_NOTIFICATION_ID = 4567654;
private void showNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification = new Notification.Builder(this)
.setContentTitle(getText(R.string.app_name))
.setContentText("")
.setSmallIcon(R.mipmap.ic_notification)
.setContentIntent(pendingIntent)
.setTicker("")
.build();
startForeground(LOCATION_SERVICE_NOTIFICATION_ID, notification);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
setUpLocationClientIfNeeded();
startLocationServices();
}
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
protected synchronized void buildGoogleApiClient() {
this.mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
return true;
} else {
return false;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
/*
WakeLock is reference counted so we don't want to create multiple WakeLocks. So do a check before initializing and acquiring.
This will fix the "java.lang.Exception: WakeLock finalized while still held: MyWakeLock" error that you may find.
*/
if (this.mWakeLock == null) { //**Added this
this.mWakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
}
if (!this.mWakeLock.isHeld()) { //**Added this
this.mWakeLock.acquire();
}
if (!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
setUpLocationClientIfNeeded();
if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress) {
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
private void setUpLocationClientIfNeeded() {
if (mGoogleApiClient == null)
buildGoogleApiClient();
}
#Override
public void onDestroy() {
stopLocationServices();
super.onDestroy();
}
private void startLocationServices() {
mInProgress = false;
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval to 5 seconds
mLocationRequest.setInterval(5000);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(1000);
mLocationRequest.setSmallestDisplacement(0);
servicesAvailable = servicesConnected();
}
private void stopLocationServices() {
// Turn off the request flag
this.mInProgress = false;
if (this.servicesAvailable && this.mGoogleApiClient != null) {
this.mGoogleApiClient.unregisterConnectionCallbacks(this);
this.mGoogleApiClient.unregisterConnectionFailedListener(this);
this.mGoogleApiClient.disconnect();
// Destroy the current location client
this.mGoogleApiClient = null;
}
// Display the connection status
// Toast.makeText(this, DateFormat.getDateTimeInstance().format(new Date()) + ":
// Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
if (this.mWakeLock != null) {
this.mWakeLock.release();
this.mWakeLock = null;
}
}
private void cancelNotification() {
NotificationManager nMgr = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
nMgr.cancel(LOCATION_SERVICE_NOTIFICATION_ID);
}
#Override
public void onLocationChanged(Location location) {
// log the new location
}
#Override
public void onConnected(#Nullable Bundle bundle) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_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.
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient,
mLocationRequest, this); // This is the changed line.
}
#Override
public void onConnectionSuspended(int i) {
// Turn off the request flag
mInProgress = false;
// Destroy the current location client
mGoogleApiClient = null;
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
mInProgress = false;
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution()) {
// If no resolution is available, display an error dialog
} else {
}
}
}
I'm not sure I have your answer but since you're 9 days in with nothing I'll give some suggestions.
My app is doing what you would like to do. I use a long running started Service to keep location updated even when the phone is off.
The difference most likely to cause different behavior between your code & mine is the return from onStartCommand(). You are returning START_STICKY. This is the recommended return for something like this:
This mode makes sense for things that will be explicitly started and
stopped to run for arbitrary periods of time, such as a service
performing background music playback.
However, I'm sending info in the Intent that I needed to have redelivered so I'm returning START_REDELIVER_INTENT. Try this (even if you have no need to redeliver any data) to see if it fixes your problem.
Also, I didn't need WakeLock in my implementation. Maybe your implementation needs this though. Have you tried without it?
Edit: Lastly, what kind of device are you using? link

'double android.location.Location.getLatitude()' on a null object reference ParseGeoPoints

I want to query parse to return list of names stored on the Parse Cloud. I am implemented geoLocation and I am using getLatitude and getLongitutude to ParseGeoPoint to get the list of names.
public class TakePhotoActivity extends BaseActivity implements RevealBackgroundView.OnStateChangeListener, LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
CameraHostProvider {
public static final String ARG_REVEAL_START_LOCATION = "reveal_start_location";
private static final Interpolator ACCELERATE_INTERPOLATOR = new AccelerateInterpolator();
private static final Interpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator();
private static final int STATE_TAKE_PHOTO = 0;
private static final int STATE_SETUP_PHOTO = 1;
/*
* Define a request code to send to Google Play services This code is returned in
* Activity.onActivityResult
*/
private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
/*
* Constants for location update parameters
*/
// Milliseconds per second
private static final int MILLISECONDS_PER_SECOND = 1000;
// The update interval
private static final int UPDATE_INTERVAL_IN_SECONDS = 5;
// A fast interval ceiling
private static final int FAST_CEILING_IN_SECONDS = 1;
// Update interval in milliseconds
private static final long UPDATE_INTERVAL_IN_MILLISECONDS = MILLISECONDS_PER_SECOND
* UPDATE_INTERVAL_IN_SECONDS;
// A fast ceiling of update intervals, used when the app is visible
private static final long FAST_INTERVAL_CEILING_IN_MILLISECONDS = MILLISECONDS_PER_SECOND
* FAST_CEILING_IN_SECONDS;
#Bind(R.id.vRevealBackground)
RevealBackgroundView vRevealBackground;
#Bind(R.id.vPhotoRoot)
View vTakePhotoRoot;
#Bind(R.id.vShutter)
View vShutter;
#Bind(R.id.ivTakenPhoto)
ImageView ivTakenPhoto;
#Bind(R.id.vUpperPanel)
ViewSwitcher vUpperPanel;
#Bind(R.id.vLowerPanel)
ViewSwitcher vLowerPanel;
#Bind(R.id.cameraView)
CameraView cameraView;
#Bind(R.id.rvFilters)
RecyclerView rvFilters;
#Bind(R.id.btnTakePhoto)
Button btnTakePhoto;
private float radius;
private float lastRadius;
private boolean pendingIntro;
private int currentState;
private Location lastLocation;
private Location currentLocation;
// A request to connect to Location Services
private LocationRequest locationRequest;
// Stores the current instantiation of the location client in this object
private GoogleApiClient locationClient;
private File photoPath;
public static void startCameraFromLocation(int[] startingLocation, Activity startingActivity) {
Intent intent = new Intent(startingActivity, TakePhotoActivity.class);
intent.putExtra(ARG_REVEAL_START_LOCATION, startingLocation);
startingActivity.startActivity(intent);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_take_photo);
updateStatusBarColor();
updateState(STATE_TAKE_PHOTO);
setupRevealBackground(savedInstanceState);
setupPhotoFilters();
radius = InstaMaterialApplication.getSearchDistance();
lastRadius = radius;
// Create a new global location parameters object
locationRequest = LocationRequest.create();
// Set the update interval
locationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
// Use high accuracy
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the interval ceiling to one minute
locationRequest.setFastestInterval(FAST_INTERVAL_CEILING_IN_MILLISECONDS);
// Create a new location client, using the enclosing class to handle callbacks.
locationClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
vUpperPanel.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
vUpperPanel.getViewTreeObserver().removeOnPreDrawListener(this);
pendingIntro = true;
vUpperPanel.setTranslationY(-vUpperPanel.getHeight());
vLowerPanel.setTranslationY(vLowerPanel.getHeight());
return true;
}
});
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void updateStatusBarColor() {
if (Utils.isAndroid5()) {
getWindow().setStatusBarColor(0xff111111);
}
}
private void setupRevealBackground(Bundle savedInstanceState) {
vRevealBackground.setFillPaintColor(0xFF16181a);
vRevealBackground.setOnStateChangeListener(this);
if (savedInstanceState == null) {
final int[] startingLocation = getIntent().getIntArrayExtra(ARG_REVEAL_START_LOCATION);
vRevealBackground.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
vRevealBackground.getViewTreeObserver().removeOnPreDrawListener(this);
vRevealBackground.startFromLocation(startingLocation);
return true;
}
});
} else {
vRevealBackground.setToFinishedFrame();
}
}
FiltersQueryAdapter mainAdapter = new FiltersQueryAdapter(this, PhotoFiltersAdapter.class
, new ParseRecyclerQueryAdapter.QueryFactory() {
public ParseQuery create() {
Location myLoc = (currentLocation == null) ? lastLocation : currentLocation;
ParseQuery query = ParseQuery.getQuery("PlaceFilters");
//query.include("user");
query.orderByAscending("GeoArea");
query.whereWithinKilometers("GeoArea", geoPointFromLocation(myLoc), radius);
query.setLimit(6);
return query;
}
});
private void setupPhotoFilters() {
rvFilters.setHasFixedSize(true);
rvFilters.setAdapter(mainAdapter);
rvFilters.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
}
#Override
protected void onResume() {
super.onResume();
cameraView.onResume();
// Get the latest search distance preference
radius = InstaMaterialApplication.getSearchDistance();
// Checks the last saved location to show cached data if it's available
if (lastLocation != null) {
// If the search distance preference has been changed, move
// map to new bounds.
if (lastRadius != radius) {
// Save the current radius
lastRadius = radius;
doListQuery();
}
}
}
#Override
protected void onPause() {
super.onPause();
cameraView.onPause();
}
/*
* Called when the Activity is no longer visible at all. Stop updates and disconnect.
*/
#Override
public void onStop() {
// If the client is connected
if (locationClient.isConnected()) {
stopPeriodicUpdates();
}
// After disconnect() is called, the client is considered "dead".
locationClient.disconnect();
super.onStop();
}
/*
* Called when the Activity is restarted, even before it becomes visible.
*/
#Override
public void onStart() {
super.onStart();
// Connect to the location services client
locationClient.connect();
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
// Choose what to do based on the request code
switch (requestCode) {
// If the request code matches the code sent in onConnectionFailed
case CONNECTION_FAILURE_RESOLUTION_REQUEST:
switch (resultCode) {
// If Google Play services resolved the problem
case Activity.RESULT_OK:
if (InstaMaterialApplication.APPDEBUG) {
// Log the result
Log.d(InstaMaterialApplication.APPTAG, "Connected to Google Play services");
}
break;
// If any other result was returned by Google Play services
default:
if (InstaMaterialApplication.APPDEBUG) {
// Log the result
Log.d(InstaMaterialApplication.APPTAG, "Could not connect to Google Play services");
}
break;
}
// If any other request code was received
default:
if (InstaMaterialApplication.APPDEBUG) {
// Report that this Activity received an unknown requestCode
Log.d(InstaMaterialApplication.APPTAG, "Unknown request code received for the activity");
}
break;
}
}
#OnClick(R.id.btnTakePhoto)
public void onTakePhotoClick() {
btnTakePhoto.setEnabled(false);
cameraView.takePicture(true, true);
animateShutter();
}
#OnClick(R.id.btnAccept)
public void onAcceptClick() {
PublishActivity.openWithPhotoUri(this, Uri.fromFile(photoPath));
}
/*
* Verify that Google Play services is available before making a request.
*
* #return true if Google Play services is available, otherwise false
*/
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
if (InstaMaterialApplication.APPDEBUG) {
// In debug mode, log the status
Log.d(InstaMaterialApplication.APPTAG, "Google play services available");
}
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Display an error dialog
Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode, this, 0);
if (dialog != null) {
ErrorDialogFragment errorFragment = new ErrorDialogFragment();
errorFragment.setDialog(dialog);
errorFragment.show(getSupportFragmentManager(), InstaMaterialApplication.APPTAG);
}
return false;
}
}
/*
* Called by Location Services when the request to connect the client finishes successfully. At
* this point, you can request the current location or start periodic updates
*/
public void onConnected(Bundle bundle) {
if (InstaMaterialApplication.APPDEBUG) {
Log.d("Location Connected", InstaMaterialApplication.APPTAG);
}
currentLocation = getLocation();
startPeriodicUpdates();
}
/*
* Called by Location Services if the connection to the location client drops because of an error.
*/
public void onDisconnected() {
if (InstaMaterialApplication.APPDEBUG) {
Log.d("Location Disconnected", InstaMaterialApplication.APPTAG);
}
}
#Override
public void onConnectionSuspended(int i) {
Log.i(InstaMaterialApplication.APPTAG, "GoogleApiClient connection has been suspend");
}
/*
* Called by Location Services if the attempt to Location Services fails.
*/
public void onConnectionFailed(ConnectionResult connectionResult) {
// Google Play services can resolve some errors it detects. If the error has a resolution, try
// sending an Intent to start a Google Play services activity that can resolve error.
if (connectionResult.hasResolution()) {
try {
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(this, CONNECTION_FAILURE_RESOLUTION_REQUEST);
} catch (IntentSender.SendIntentException e) {
if (InstaMaterialApplication.APPDEBUG) {
// Thrown if Google Play services canceled the original PendingIntent
Log.d(InstaMaterialApplication.APPTAG, "An error occurred when connecting to location services.", e);
}
}
} else {
// If no resolution is available, display a dialog to the user with the error.
showErrorDialog(connectionResult.getErrorCode());
}
}
/*
* Report location updates to the UI.
*/
public void onLocationChanged(Location location) {
currentLocation = location;
if (lastLocation != null
&& geoPointFromLocation(location)
.distanceInKilometersTo(geoPointFromLocation(lastLocation)) < 0.01) {
// If the location hasn't changed by more than 10 meters, ignore it.
return;
}
lastLocation = location;
// Update map radius indicator
doListQuery();
}
/*
* In response to a request to start updates, send a request to Location Services
*/
private void startPeriodicUpdates() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_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.
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(
locationClient, locationRequest, this);
}
/*
* In response to a request to stop updates, send a request to Location Services
*/
private void stopPeriodicUpdates() {
locationClient.disconnect();
}
/*
* Get the current location
*/
private Location getLocation() {
// If Google Play Services is available
if (servicesConnected()) {
// Get the current location
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_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.
}
return LocationServices.FusedLocationApi.getLastLocation(locationClient);
} else {
return null;
}
}
/*
* Set up a query to update the list view
*/
private void doListQuery() {
Location myLoc = (currentLocation == null) ? lastLocation : currentLocation;
// If location info is available, load the data
if (myLoc != null) {
// Refreshes the list view with new data based
// usually on updated location data.
// mainAdapter.;
}
}
/*
* Helper method to get the Parse GEO point representation of a location
*/
private ParseGeoPoint geoPointFromLocation(Location loc) {
return new ParseGeoPoint(loc.getLatitude(), loc.getLongitude());
}
/*
* Show a dialog returned by Google Play services for the connection error code
*/
private void showErrorDialog(int errorCode) {
// Get the error dialog from Google Play services
Dialog errorDialog =
GooglePlayServicesUtil.getErrorDialog(errorCode, this,
CONNECTION_FAILURE_RESOLUTION_REQUEST);
// If Google Play services can provide an error dialog
if (errorDialog != null) {
// Create a new DialogFragment in which to show the error dialog
ErrorDialogFragment errorFragment = new ErrorDialogFragment();
// Set the dialog in the DialogFragment
errorFragment.setDialog(errorDialog);
// Show the error dialog in the DialogFragment
errorFragment.show(getSupportFragmentManager(), InstaMaterialApplication.APPTAG);
}
}
/*
* Define a DialogFragment to display the error dialog generated in showErrorDialog.
*/
public static class ErrorDialogFragment extends DialogFragment {
// Global field to contain the error dialog
private Dialog mDialog;
/**
* Default constructor. Sets the dialog field to null
*/
public ErrorDialogFragment() {
super();
mDialog = null;
}
/*
* Set the dialog to display
*
* #param dialog An error dialog
*/
public void setDialog(Dialog dialog) {
mDialog = dialog;
}
/*
* This method must return a Dialog to the DialogFragment.
*/
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return mDialog;
}
}
private void animateShutter() {
vShutter.setVisibility(View.VISIBLE);
vShutter.setAlpha(0.f);
ObjectAnimator alphaInAnim = ObjectAnimator.ofFloat(vShutter, "alpha", 0f, 0.8f);
alphaInAnim.setDuration(100);
alphaInAnim.setStartDelay(100);
alphaInAnim.setInterpolator(ACCELERATE_INTERPOLATOR);
ObjectAnimator alphaOutAnim = ObjectAnimator.ofFloat(vShutter, "alpha", 0.8f, 0f);
alphaOutAnim.setDuration(200);
alphaOutAnim.setInterpolator(DECELERATE_INTERPOLATOR);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playSequentially(alphaInAnim, alphaOutAnim);
animatorSet.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
vShutter.setVisibility(View.GONE);
}
});
animatorSet.start();
}
#Override
public void onStateChange(int state) {
if (RevealBackgroundView.STATE_FINISHED == state) {
vTakePhotoRoot.setVisibility(View.VISIBLE);
if (pendingIntro) {
startIntroAnimation();
}
} else {
vTakePhotoRoot.setVisibility(View.INVISIBLE);
}
}
private void startIntroAnimation() {
vUpperPanel.animate().translationY(0).setDuration(400).setInterpolator(DECELERATE_INTERPOLATOR);
vLowerPanel.animate().translationY(0).setDuration(400).setInterpolator(DECELERATE_INTERPOLATOR).start();
}
#Override
public CameraHost getCameraHost() {
return new MyCameraHost(this);
}
class MyCameraHost extends SimpleCameraHost {
private Camera.Size previewSize;
public MyCameraHost(Context ctxt) {
super(ctxt);
}
#Override
public boolean useFullBleedPreview() {
return true;
}
#Override
public Camera.Size getPictureSize(PictureTransaction xact, Camera.Parameters parameters) {
return previewSize;
}
#Override
public Camera.Parameters adjustPreviewParameters(Camera.Parameters parameters) {
Camera.Parameters parameters1 = super.adjustPreviewParameters(parameters);
previewSize = parameters1.getPreviewSize();
return parameters1;
}
#Override
public void saveImage(PictureTransaction xact, final Bitmap bitmap) {
runOnUiThread(new Runnable() {
#Override
public void run() {
showTakenPicture(bitmap);
}
});
}
#Override
public void saveImage(PictureTransaction xact, byte[] image) {
super.saveImage(xact, image);
photoPath = getPhotoPath();
}
}
private void showTakenPicture(Bitmap bitmap) {
vUpperPanel.showNext();
vLowerPanel.showNext();
ivTakenPhoto.setImageBitmap(bitmap);
updateState(STATE_SETUP_PHOTO);
}
#Override
public void onBackPressed() {
if (currentState == STATE_SETUP_PHOTO) {
btnTakePhoto.setEnabled(true);
vUpperPanel.showNext();
vLowerPanel.showNext();
updateState(STATE_TAKE_PHOTO);
} else {
super.onBackPressed();
}
}
private void updateState(int state) {
currentState = state;
if (currentState == STATE_TAKE_PHOTO) {
vUpperPanel.setInAnimation(this, R.anim.slide_in_from_right);
vLowerPanel.setInAnimation(this, R.anim.slide_in_from_right);
vUpperPanel.setOutAnimation(this, R.anim.slide_out_to_left);
vLowerPanel.setOutAnimation(this, R.anim.slide_out_to_left);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ivTakenPhoto.setVisibility(View.GONE);
}
}, 400);
} else if (currentState == STATE_SETUP_PHOTO) {
vUpperPanel.setInAnimation(this, R.anim.slide_in_from_left);
vLowerPanel.setInAnimation(this, R.anim.slide_in_from_left);
vUpperPanel.setOutAnimation(this, R.anim.slide_out_to_right);
vLowerPanel.setOutAnimation(this, R.anim.slide_out_to_right);
ivTakenPhoto.setVisibility(View.VISIBLE);
}
}
}
Here is my Logcat:
02-16 17:32:40.700 22343-22343/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.google.peep, PID: 22343
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.google.peep/com.google.peep.activity.TakePhotoActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'double android.location.Location.getLatitude()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2484)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2544)
at android.app.ActivityThread.access$900(ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1394)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:168)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'double android.location.Location.getLatitude()' on a null object reference
at com.google.peep.activity.TakePhotoActivity.geoPointFromLocation(TakePhotoActivity.java:501)
at com.google.peep.activity.TakePhotoActivity.access$300(TakePhotoActivity.java:66)
at com.google.peep.activity.TakePhotoActivity$3.create(TakePhotoActivity.java:223)
at com.javon.parserecyclerviewadapter.ParseRecyclerQueryAdapter.loadParseData(ParseRecyclerQueryAdapter.java:96)
at com.javon.parserecyclerviewadapter.ParseRecyclerQueryAdapter.registerAdapterDataObserver(ParseRecyclerQueryAdapter.java:176)
at android.support.v7.widget.RecyclerView.setAdapterInternal(RecyclerView.java:886)
at android.support.v7.widget.RecyclerView.setAdapter(RecyclerView.java:847)
at com.google.peep.activity.TakePhotoActivity.setupPhotoFilters(TakePhotoActivity.java:232)
at com.google.peep.activity.TakePhotoActivity.onCreate(TakePhotoActivity.java:152)
Your getlocation is before the google api client actually connects,pass setup photofilters in onConnected and it will run.Tatasthu!

Categories

Resources