How to call fusedLocationClient.getLastLocation() from within a JobService? - java

I'm trying to implement getLastLocation().addOnSuccessListener as documented here: https://developer.android.com/training/location/retrieve-current#last-known but in my case within a JobService which is periodically ran by the JobScheduler.
Here is the meaningful part of my jobService:
public class PeriodicJob extends JobService {
final String TAG = "BNA.Job";
private FusedLocationProviderClient fusedLocationClient;
#Override
public void onCreate() {
super.onCreate();
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
Log.i(TAG, "onCreate");
}
#Override
public boolean onStartJob(JobParameters params) {
Log.d(TAG, "Job started..."); // logs every 15mins
fusedLocationClient.getLastLocation()
.addOnSuccessListener(this, new OnSuccessListener<Location>() {
#Override
public void onSuccess(Location location) {
// Got last known location. In some rare situations this can be null.
if (location != null) {
Log.d(TAG, "got Location: " + location);
Log.d(TAG, "Accuracy:" + location.getAccuracy()+", LON:"
+location.getLongitude() + ", LAT:" + location.getLatitude());
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
(...) // never runs
});
return true;
}
When I run this (it compiles fine), I get: Caused by: java.lang.ClassCastException: (...)PeriodicJob cannot be cast to java.util.concurrent.Executor
I looked at Android app crashes on firebase phone authentication and Can't cast this into Executor in google reCAPTCHA but I still couldnt get it working. Just calling .addOnSuccessListener(new OnSuccessListener<Location>() like the second question's answer suggests, throws no error, but the code is never executed (at least, no log is written).
How can I get the call to onSuccess working?
EDIT: In fact it was working all the time (without the first parameter), I had neglected to detect the case when a null location was returned (which I wasnt expecting). Thanks to Mike M.!

Related

Performing Callbacks one after another

I am new to doing asynchronous programming in Android Java. I am wondering if there is a way to run another Callback after an initial Callback function has completed. Right now, I think they are running in parallel even though the second relies on the first.
First Callback:
// GETTING USER
private interface FirestoreUserCallback {
void onCallback (User myUser);
}
private void getUser(final FirestoreUserCallback firestoreCallback) {
Task<DocumentSnapshot> task = fStore.collection("users").document(fAuth.getCurrentUser().getUid()).get();
task.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
user = documentSnapshot.toObject(User.class);
firestoreCallback.onCallback(user);
Log.d(TAG, "user created");
}
});
task.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "user creation failed");
}
});
}
Second Callback:
// GETTING ALL DOCUMENTS
private interface FirestoreDocumentCallback {
void onCallback (List<TableEntries> myEntries);
}
private void getDocuments (final FirestoreDocumentCallback firestoreDocumentCallback) {
fStore.collection("result")
.document(Integer.toString(user.getCompanyNumber())) // need to use User object returned from the first Callback
.collection("SAM").get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
List<TableEntries> results = new ArrayList<>();
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
// add objects to results ArrayList ...
Log.d(TAG, document.getId() + " => " + document.getData());
}
firestoreDocumentCallback.onCallback(results);
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
}
onCreate:
getUser(new FirestoreUserCallback () {
#Override
public void onCallback(User myUser) {
user = myUser;
}
});
getDocuments(new FirestoreDocumentCallback() {
#Override
public void onCallback(List<TableEntries> myEntries) {
entries = myEntries;
}
});
getDocuments() relies on the user variable being given its value from the first Callback. I'm receiving this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'double java.lang.Double.doubleValue()' on a null object reference
Callbacks are looking fine. You just need to check if your value is null or not before accessing it. Just add a null check
if(doubleValue!=null)
Using RxJava. First, we fetch the user and then fetch the documents. Rx-Java has an operator flatmap. flatmap is used to execute the sequential tasks, where the second task is dependent on the data from the first task.
final CompositeDisposable disposable = new CompositeDisposable();
//function to fetch user data
Single<User> getUser(){
return API.getUserData(...);
}
//function to fetch ducuments
Sinlge<UserDetail> getDocuments(int userId){
return API.getUserDetail(userId, ...);
}
//Subscribe
disposable.add(getUser()
.flatmap(user-> return getDocuments(...))
.subscribeOn(Scheduler.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObservable(){
#Override
public void onSuccess(UserDetail userDetail){
Log.v("Api result", "Successful";
//Do some work
}
#Override
public void onError(Throwable e)
Log.v("Api result", "Error Returned");
}
}));
If either of the API call fails, onError() is called. If first API fails, second API call is not executed and onError() is called.
The simplest solution for your use-case is to pass both queries to Tasks.whenAllSuccess() method, as explained in my answer from the following post:
Firestore - Merging two queries locally
So once the task is complete, you can use the elements from both queries. Another solution might be to use Android Jetpack with LiveData along with ViewModel, as the Android team recommends.

Not understanding RxJava MaybeObserver (chat notification not working)

I'm very new to Java, android and Rxjava. I recently noticed that in an existing project (not written by me) a chat notification that is supposed to be received isn't received. Thus I started to do some tracing. Below is part of the code.
Note: Notifications that do get received seems to always go to onSuccess in the file FCMServices
I've put breakpoints pretty much everywhere in the code below. What I noticed was for the notifications that I do not receive onSuccess and onError do not get called but onComplete does. However I find that strange as I thought either onSuccess or onError must be called before onComplete.
My understanding of those functions is based on http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html
//FCMService.java
currentConversationRepo.getCurrentConversation()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MaybeObserver<CurrentConversation>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
currentChatDisposable = d;
}
#Override
public void onSuccess(#NonNull CurrentConversation currentConversation) {
System.out.println("This is SUCCESS");
if (channelSid == null && author == null && usedAdId == null){
buildNotifyNotification(body, action, "", userId);
}
if (channelSid != null && author != null) {
if (!channelSid.equals(currentConversation.getChannelSid())) {
createChatNotification(author, channelSid, body);
}
}
currentChatDisposable.dispose();
}
#Override
public void onError(#NonNull Throwable e) {
System.out.println("Error getting current conversation: " + e.getMessage());
currentChatDisposable.dispose();
}
#Override
public void onComplete() {
System.out.println("This is onComplete");
currentChatDisposable.dispose();
}
});
I then started to do some tracing of where onComplete was called and appears that it was called by another onSuccess from the class TestObserver in reactivex.io
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/observers/TestObserver.html
//TestObserver.java
#Override
public void onSuccess(T value) {
onNext(value);
onComplete();
}
Which was in turn called by the onSuccess in MaybeFlatMapBiSelector class. (Also a reactivex.io class I believe)
//MaybeFlatMapBiSSelector.java
#Override
public void onSuccess(U value) {
T t = this.value;
this.value = null;
R r;
try {
r = ObjectHelper.requireNonNull(resultSelector.apply(t, value), "The resultSelector returned a null value");
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
actual.onError(ex);
return;
}
actual.onSuccess(r);
}
This turned out to be from the MaybeObserver interface
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html#onComplete--
My question is what exactly are the onSuccess of TestObserver and MaybeFlatMapBiSelector doing? And if it is even possible based on the information I have provided, why is it that some notifications goes to onComplete without going to onSuccess or onError in FCMServices.java
Have you tried to comment currentChatDisposable.dispose(); ? I've had the same issue not long ago where I was disposing of my disposable too early and no data where showing
Usually you call .dispose() when onPause() or onDestroy() of the lifecycle
PS: In case you didn't know Maybe in RxJava return either a single value, nothing at all or an exception.

FireStore OnFailureListener is not called

I am searching some objects with their names in the FIRESTORE database and i have attached an Onsuccess and onfailure Listener to my get method but if some objects are not found it does not execute the body of onfailure
i have tried debuging it but failed to understand the issue.
i have also searched through many articles but it seems no one has encountered this type of error before.I have attached the screenshot from my logcat
below is my code
#Override
public void onResponse(Call<List<DeviceEntity>> call, final Response<List<DeviceEntity>> response) {
if (response.body() != null) {
Log.i("Test", "onResponse: got the device name searching it in firestore");
for (int i = 0; i < response.body().size(); i++) {
final int finalI = i;
Log.i("Test", "Devices that matched the saerch"+ response.body().get(finalI).getDeviceName());
FirebaseFirestore firebaseFirestore=FirebaseFirestore.getInstance();
firebaseFirestore.collection("Mobiles").document(response.body().get(finalI).getDeviceName())
.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
MobileDataBaseObjectClass mb=documentSnapshot.toObject(MobileDataBaseObjectClass.class);
if (mb != null) {
mb.setTimeStamp(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()));
Log.i(TAGo, "Success:getting the info from firestore");
new insertAsyncTask(mdao,"firestore").execute(mb);
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.i(TAGo,"Onfailure listner"+e.toString());
}
});
enter image description here
the comment by Frank van Puffelen is the right answer.
onFailure only executes when there was a problem reading the document (e.g. you don't have access to it). If the document doesn't exist, it should trigger onSuccess. – Frank van Puffelen

How to remove FusedlocationProviderClient location updates from within non-activity class

I’m trying to write a utility class to wrap the Google Play Services FusedLocationProviderClient API and location permissions request as I’m sick of writing all that boilerplate every time I want to add location functionality to an app. The problem I’m having though is I’m unable to remove location updates once I’ve started them. Here’s the relevant bits of my utility class:
public class UserLocationUtility extends LocationCallback
{
// Hold a WeakReference to the host activity (allows it to be garbage-collected to prevent possible memory leak)
private final WeakReference<Activity> weakActivity;
// Debug tag
private static final String TAG = "UserLocationUtility";
public static class RequestCodes
{
static final int CURRENT_LOCATION_ONE_TIME = 0;
static final int CURRENT_LOCATION_UPDATES = 1;
static final int LAST_KNOWN_LOCATION = 2;
static final int SMART_LOCATION = 3;
}
private FusedLocationProviderClient mLocationClient;
private Context mContext;
private LocationRequest mLocationRequest;
/* Constructor */
UserLocationUtility(Activity activity){
// assign the activity to the weak reference
this.weakActivity = new WeakReference<>(activity);
// Hold a reference to the Application Context
this.mContext = activity.getApplicationContext();
// Instantiate our location client
this.mLocationClient = LocationServices.getFusedLocationProviderClient(mContext);
// Set up the default LocationRequest parameters
this.mLocationRequest = new LocationRequest();
setLocationRequestParams(2000, 500, LocationRequest.PRIORITY_HIGH_ACCURACY);
// Sets up the LocationRequest with an update interval of 30 seconds, a fastest
// update interval cap of 5 seconds and using balanced power accuracy priority.
} /* Note: values for testing only. Will be dialed back for better power management when testing complete */
/* Stripped out other methods for brevity */
#SuppressLint("MissingPermission")
public void getCurrentLocationOneTime(final UserLocationCallback callback){
mLocationClient.requestLocationUpdates(mLocationRequest, new LocationCallback()
{
#Override
public void onLocationResult(LocationResult locationResult){
if (locationResult == null){
callback.onFailedRequest("getCurrentLocationOneTime(): Request failed: returned null");
return;
}
callback.onLocationResult(locationResult.getLastLocation());
stopLocationUpdates(); /* Stopping location updates here just for testing (NOT WORKING!!) */
}
}, null);
}
public void stopLocationUpdates(){
mLocationClient.removeLocationUpdates(new LocationCallback(){});
Log.i(TAG, "stopLocationUpdates(): Location updates removed");
}
}
Here’s how I’m trying to use it (from MainActivity):
UserLocationUtility locationUtility;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
locationUtility = new UserLocationUtility(this);
if (locationUtility.checkPermissionGranted()){
Log.i(TAG, "Permissions are granted.");
getLocationUpdates();
} else {
Log.i(TAG, "Permissions are not granted. Attempting to request...");
locationUtility.requestPermissions(UserLocationUtility.RequestCodes.CURRENT_LOCATION_UPDATES);
}
}
public void getLocationUpdates(){
locationUtility.getCurrentLocationOneTime(new UserLocationCallback() {
#Override
public void onLocationResult(Location location) {
Log.i(TAG, "getLocationUpdates result: " + location.toString());
}
#Override
public void onFailedRequest(String result) {
Log.e(TAG, "LocationUpdates result: " + result);
}
});
}
And here's a sample from the log:
I/MainActivity: getLocationUpdates result: Location[fused 34.421998,-125.084000 hAcc=731 et=+2h10m52s694ms vAcc=??? sAcc=??? bAcc=???]
I/UserLocationUtility: stopLocationUpdates(): Location updates removed
I/MainActivity: getLocationUpdates result: Location[fused 34.421998,-125.084000 hAcc=739 et=+2h10m57s697ms vAcc=??? sAcc=??? bAcc=???]
I/UserLocationUtility: stopLocationUpdates(): Location updates removed
I/MainActivity: getLocationUpdates result: Location[fused 34.421998,-125.084000 hAcc=763 et=+2h11m5s723ms vAcc=??? sAcc=??? bAcc=???]
I/UserLocationUtility: stopLocationUpdates(): Location updates removed
etc...
As you can see I’m receiving the location updates correctly but the call to stopLocationUpdates() isn’t working. I have a feeling it’s something to do with the fact that I’m passing a new LocationCallback to the removeUpdates() method, but I’m not sure what the alternative is, or even if there is an alternative. This being a non-activity class I can’t exactly initialise LocationCallback as a member in onCreate() then pass it around as needed. The google docs on this aren’t much help at all. Whether that’s because I lack the necessary understanding to decipher them or because they’re just not very good I don’t know but either way I’m stumped and having searched around a lot I can’t seem to find an existing answer elsewhere.
Thanks.
Posting my solution as an answer in case it helps anyone else.
I got it working by declaring a LocationCallback as a member variable and then initialising (or re-initialising) it in each method that requires it...
public void getCurrentLocationUpdates(final UserLocationCallback callback){
if (mIsReceivingUpdates){
callback.onFailedRequest("Device is already receiving updates");
return;
}
// Set up the LocationCallback for the request
mLocationCallback = new LocationCallback()
{
#Override
public void onLocationResult(LocationResult locationResult){
if (locationResult != null){
callback.onLocationResult(locationResult.getLastLocation());
} else {
callback.onFailedRequest("Location request returned null");
}
}
};
// Start the request
mLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null);
// Update the request state flag
mIsReceivingUpdates = true;
}
I check at the beginning of the method whether or not location updates are already being received and get out early if so. This prevents duplicate (and thus unstoppable) location update requests being initiated.
Calling the stopLocationUpdates (below for reference) method now works as it should.
public void stopLocationUpdates(){
mLocationClient.removeLocationUpdates(mLocationCallback);
mIsReceivingUpdates = false;
Log.i(TAG, "Location updates removed");
}

Can't find "this" class NoClassDefFoundError - Android

I'm trying very simply to test the 3.0 Facebook get started guide. https://developers.facebook.com/docs/getting-started/facebook-sdk-for-android/3.0/
I have had some problems with imports and references but I don't know it that's relevant. The issue I'm having is when I try to run the test Activity ant get this error:
Could not find class 'com.test1.test2.FacebookLogin$1', referenced from method com.test1.test2.FacebookLogin.onCreate
Code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_facebook_login);
//Error occurs here when I use 'this'
Session.openActiveSession(this, true, new Session.StatusCallback() {
// callback when session changes state
#Override
public void call(Session session, SessionState state, Exception exception) {
if (session.isOpened()) {
// make request to the /me API
Request.executeMeRequestAsync(session, new Request.GraphUserCallback() {
// callback after Graph API response with user object
#Override
public void onCompleted(GraphUser user, Response response) {
if (user != null) {
TextView welcome = (TextView) findViewById(R.id.welcome);
welcome.setText("Hello " + user.getName() + "!");
}
}
});
}
}
});
}
How can I get a NoClassDefFoundError when I use this?
UPDATE:
After testing with the Scrumtious tutorial https://developers.facebook.com/docs/tutorials/androidsdk/3.0/scrumptious/authenticate/ as well I'm getting the same error when I call the Session.StatusCallback() method. I still don't know what my problem is though.
Thanks for the help
Maybe you updated to Android sdk tools revision 22. There is an issue there. You have to add the libraries you use in the export path. Check:
Android Sdk tools Revision 22 issue?

Categories

Resources