Stop initial triggering if device is already inside Geofence android - android

I am using Geofencing on android, and when creating a geo-fence the device is getting notified if the user was already inside the geo-fence, which is not the behavior I'm looking for, I only want to notify on transition ENTER and EXIT.
This is how I'm creating the geofence request:
private GeofencingRequest createGeoFenceRequest(List<Geofence> geofenceList) {
return new GeofencingRequest.Builder()
//The INITIAL_TRIGGER_ENTER is used to notify the user initially if he/she/other
//is already inside the geo-fence zone
//.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
.addGeofences(geofenceList)
.build();
}
And inside my GeofenceTransitionService.java:
#Override
protected void onHandleIntent(#Nullable Intent intent) {
if (intent != null) {
geofencingEvent = GeofencingEvent.fromIntent(intent);
// Retrieve the transition type.
geoFenceTransition = geofencingEvent.getGeofenceTransition();
}
}
The transition type is returned as Geofence.GEOFENCE_TRANSITION_ENTER even though I am not entering, I am already inside the geofence.
Anyone know how I can stop this initial trigger ?

Why you got the initial trigger
tl;dr
It's the default behavior
I got here with a similar problem of yours:
The transition type is returned as Geofence.GEOFENCE_TRANSITION_ENTER even though I am not entering, I am already inside the geofence.
After some research, I found this behavior stated on the GeofencingRequest.Builder documentation:
Public Methods
public GeofencingRequest.Builder setInitialTrigger (int initialTrigger)
Sets the geofence notification behavior at the moment when the geofences are added. The default behavior is INITIAL_TRIGGER_ENTER and INITIAL_TRIGGER_DWELL.
To understand what those constants actually mean, we need to check the documentation for the enclosing class, GeofencingRequest:
Constant Summary
int INITIAL_TRIGGER_DWELL
A flag indicating that geofencing service should trigger GEOFENCE_TRANSITION_DWELL notification at the moment when the geofence is added and if the device is already inside that geofence for some time.
int INITIAL_TRIGGER_ENTER
A flag indicating that geofencing service should trigger GEOFENCE_TRANSITION_ENTER notification at the moment when the geofence is added and if the device is already inside that geofence.
int INITIAL_TRIGGER_EXIT
A flag indicating that geofencing service should trigger GEOFENCE_TRANSITION_EXIT notification at the moment when the geofence is added and if the device is already outside that geofence.
This explains why you were notified of a GEOFENCE_TRANSITION_ENTER even though you didn't use setInitialTrigger() - it's the default behavior.
While investigating this, I found out something interesting. If you subscribe to entrances and departures of geofences and then set the initial trigger to INITIAL_TRIGGER_ENTER | INITIAL_TRIGGER_EXIT (bitwise OR; summing both constants together would also work), you will be notified of all the geofences you're currently inside (with GEOFENCE_TRANSITION_ENTER) and also of all the geofences you're currently outside (with GEOFENCE_TRANSITION_EXIT). I haven't thought about a good use case for this, but found it interesting anyway.
How to stop the initial trigger
tl;dr
Set the initial trigger to 0, using a constant to attribute meaning to the number (e.g., private static final int NO_INITIAL_TRIGGER = 0)
Taking a closer look into the documentation of setInitialTrigger() it's possible to read:
Parameters
initialTrigger the notification behavior. It's a bit-wise of INITIAL_TRIGGER_ENTER and/or INITIAL_TRIGGER_EXIT and/or INITIAL_TRIGGER_DWELL.
The hint of using a bitwise operation, combined with the documentation found for those constants (which work as flags for each type of transition) led me to question what would happen if all of the bits were turned off.
So I tried to pass the value of 0 to setInitialTrigger(), in order to override the default behavior and turn off all of the initial triggers without having to resort to the workaround proposed by rfn123:
private GeofencingRequest createGeofencingRequest(List<Geofence> geofences) {
return new GeofencingRequest.Builder()
.setInitialTrigger(0)
.addGeofences(geofences)
.build();
}
Or - perhaps even better - giving some meaning to the magic number:
private static final int NO_INITIAL_TRIGGER = 0;
private GeofencingRequest createGeofencingRequest(List<Geofence> geofences) {
return new GeofencingRequest.Builder()
.setInitialTrigger(NO_INITIAL_TRIGGER)
.addGeofences(geofences)
.build();
}
After some hours of testing this seems to be a sensible approach even if it relies on a poorly documented behavior (as keepTrackOfYourStack has noticed, the documentation has been updated to clarify this).
Please note: From the tests I performed, it's still possible to get an almost instant trigger for a transition, depending on chosen geofences' radii and current location's precision.
For instance, you might be slightly away from the geofence and then suddenly your position is updated, putting you inside the geofence: this will trigger a geofence entrance notification.
However, this entrance notification is not related to the initial trigger you've configured, but to the update in your perceived position.
On a leaving note, if you don't mind, I have a suggestion for an alternate way to implement the onHandleIntent() method.
Instead of checking for the intent object's nullity, just pass that responsibility to the GeofencingEvent#fromIntent() call.
You can then check the validity of the returned event object, using a guard clause - which will increase your method's readability (at least in my opinion).
This will allow you to both test for nullity and check if the event has an error.
Something like this:
#Override
protected void onHandleIntent(#Nullable Intent intent) {
GeofencingEvent event = GeofencingEvent.fromIntent(intent);
if (event == null || event.hasError()) {
logInvalidEvent(event);
return;
}
// Process the event...
}

A workaround might be passing INITIAL_TRIGGER_DWELL to the setInitialTrigger() method.
This will only trigger a notification if the device is already inside the geofence for some time. You can set this time to a very large number.
However this only works if you solely use GEOFENCE_TRANSITION_DWELL in the initial triggering and are otherwise not interested in adding a dwell notification.
Check this link: https://developers.google.com/android/reference/com/google/android/gms/location/GeofencingRequest#INITIAL_TRIGGER_ENTER

it looks like they fixed the documentation and 0 works even though there is no constant
documentation update

From the documentation
initialTrigger the notification behavior.
It's a bit-wise of GeofencingRequest.INITIAL_TRIGGER_ENTER and/or GeofencingRequest.INITIAL_TRIGGER_EXIT and/or GeofencingRequest.INITIAL_TRIGGER_DWELL. When initialTrigger is set to 0 (setInitialTrigger(0)), initial trigger would be disabled.

Related

How to access BatteryManager's BATTERY_PROPERTY_CURRENT_NOW?

I'm trying to get the current from the API. There's a function at the bottom of the documentation (https://developer.android.com/reference/android/os/BatteryManager.html) called getIntProperty, so I presume that'd be the function I'd need. However, up to now I've been simply using the getIntExtra function on an ActionBatteryChanged intent, and that same method doesn't work for this. What do I need to do different?
That function is used to get the current value, not get updates like you do with a Broadcast and an Intent. You'd call batteryManager.getIntProperty(BATTERY_PROPERTY_CURRENT_NOW) to get the value. Any of the BATTERY_PROPERTY_X constants could work, but not every phone supports all of them.
A more comprehensive code samples:
Declare this at the class level
private BatteryManager mBatteryManager = null;
onCreate()
{
mBatteryManager = (BatteryManager) getSystemService(Context.BATTERY_SERVICE);
}
Create a timer and inside the timer routine, add this
int mBatteryCurrent = mBatteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CURRENT_NOW);
The current is in uA unit. a positive integer means drawing current out of
battery and a negative integer means charging current into battery.

Geofence understanding Geofence lifetime

I've built Geofence as:
GeofenceModel modelExit = new GeofenceModel.Builder("id_oi_456")
.setTransition(Geofence.GEOFENCE_TRANSITION_DWELL)
.setExpiration(Geofence.NEVER_EXPIRE)
.setLatitude(40.414341)
.setLongitude(49.928548)
.setRadius(CLIENT_GEOFENCE_RADIUS)
.build();
SmartLocation.with(this).geofencing()
.add(modelExit)
.start(this);
When set to NEVER_EXPIRE, it gets triggered. When setExpiration() is not set geofence will not be triggered though. The question is that What isdefault expiration time for single geofence?
In regards to Android's Geofence class: it is not possible to create a Geofence using Geofence.Builder without setting an expiration. If you try to, it will throw java.lang.IllegalArgumentException: Expiration not set.
As for your GeofenceModel class, I'm not sure.
Assuming you're using com.google.android.gms.location.Geofence
underneath your GeofenceModel class.
You have an instance variable named modelExit indicates that you're waiting for the exit transition. while in your geofence configuration you're only listening to GEOFENCE_TRANSITION_DWELL. You should set the transition to GEOFENCE_TRANSITION_EXIT.

Screen tracking support - Firebase 9.8

According Firebase Android SDK Release Notes with 9.8 update we have screen tracking support with android screens and activities... The documentation says that this event works like that:
mFirebaseAnalytics.setCurrentScreen(activity,class_name,class_override_name);
In my case, I don't need overrides class name and I send null value... But i'm waiting 48h and my firebase analytics console doesn't show info about this event, any ideas?
Thanks in advance!
Another very important thing that I've noticed only after two days of intensive struggling: the setCurrentScreen method MUST be called on the UI thread.
I was only able to see that after looking for a light in the Firebase decompiled code:
#MainThread
#Keep
public final void setCurrentScreen(#NonNull Activity var1, #Size(min = 1L,max = 36L) #Nullable String var2, #Size(min = 1L,max = 36L) #Nullable String var3) {
//...
}
Whenever this method is called a event of type screen_view is logged.
And keep in mind the Firebase size restrictions. The maximum size of a screen name is 36 characters long.
First I had the same question: where is my event with current screen name on the Firebase dashboard?
I've called method mFirebaseAnalytics.setCurrentScreen(this, "MainActivity", null); with no result.
Thanks to the comment by Benoit I realized that this method indicates the value of implicit parameter that is automatically attached to any event you send.
That means it's not independent event, it's a parameter that will stick to all your events since you set it.
This will be useful if you have changing screens within single Activity. For example when you have multiple fragments with one hosting Activity. And you call this method in each fragment in onResume().
If you want to have distinct metric with the name of your screen - fire explicitly a new event for that.
Bundle params = new Bundle();
params.putString(FirebaseAnalytics.Param.ITEM_CATEGORY, "screen");
params.putString(FirebaseAnalytics.Param.ITEM_NAME, "MainActivity");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.VIEW_ITEM, params);
val bundle = Bundle()
bundle.putString(FirebaseAnalytics.Param.SCREEN_NAME, "YOUR SCREEN NAME")
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SCREEN_VIEW, bundle)
Also Firebase Analytic's screen tracking is automatic. No need for explicit separate event tracking.
Sets the current screen name, which specifies the current visual context in your app. This helps identify the areas in your app where users spend their time and how they interact with your app.
Note that screen reporting is enabled automatically and records the class name of the current Activity for you without requiring you to call this function. The class name can optionally be overridden by calling this function in the onResume callback of your Activity and specifying the screenClassOverride parameter.
If your app does not use a distinct Activity for each screen, you should call this function and specify a distinct screenName each time a new screen is presented to the user.
The name and classOverride remain in effect until the current Activity changes or a new call to setCurrentScreen is made. I will try to add this method to onResume Method. I do not know the result but i will share my experience.
firebaseAnalytics.setCurrentScreen(activity,screeenName,activity.getClass().getSimpleName());
firebaseAnalytics.setMinimumSessionDuration(100L);
params = new Bundle();
params.putString(FirebaseAnalytics.Param.ITEM_CATEGORY, "screen");
params.putString(FirebaseAnalytics.Param.ITEM_NAME, screeenName);
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.VIEW_ITEM, params);
Try using setCurrentScreen as well as manual event fire as firebase doesn't send data immediately to the console...but if event is fired up..all the remaining data is sent to firebase..
Just call that method in onResume(), and check the tracking through DebugView. it worked for me.
Check out the documentation.

SpeechRecognizer offline ERROR_NO_MATCH

SpeechRecognizer return ERROR_NO_MATCH in onResults when the device is offline while it's returning the partial results in onPartialResults() call back. The last time I played around with SpeechRecognizer it was working fine offline, I wonder if anyone has found a solution to it.
As a work around I use the partialResults returned in onPartialResults().
In the returned bundle "SpeechRecognizer.RESULTS_RECOGNITION" has all the terms minus the last term and "android.speech.extra.UNSTABLE_TEXT" has the last missing recognized term.
#Override
public void onPartialResults(Bundle partialResults) {
ArrayList<String> data = partialResults.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
ArrayList<String> unstableData = partialResults.getStringArrayList("android.speech.extra.UNSTABLE_TEXT");
mResult = data.get(0) + unstableData.get(0);
}
To make the answer a little bit more clear, you need to enable partial results first, and to call UNSTABLE_TEXT in a specific fashion:
// When creating the intent, set the partial flag to true
intent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS,true);
// When requesting results in onPartialResults(), the UNSTABLE_TEXT parameter to getSTtringArrayList() must be in quotes
ArrayList<String> unstableMatches = partialResults.getStringArrayList("android.speech.extra.UNSTABLE_TEXT");
onPartialResults() gets called multiple times now and onError() still gets called with ERROR_NO_MATCH. I ended up using a solution similar to the one listed here: https://github.com/nenick/QuAcc/blob/master/app/src/main/java/de/nenick/quacc/speechrecognition/speech/RecognizerListenerWithOfflineWorkaround.java
In a nutshell:
Keep track of partial results and whether an error was shown
Reset both in onBeginningOfSpeech()
Store partial results in the variable when onPartialResults() gets called
When onError() gets called check if result is ERROR_NO_MATCH and combine SpeechRecognizer.RESULTS_RECOGNITION with "android.speech.extra.UNSTABLE_TEXT" into your partial results variable
Call onResults()

Robotium Solo - wait for broadcast

I want to create a condition to wait for a broadcast upon a button press
right now I am just doing solo.sleep(10000)
but I dont want to sleep solo for nothing
How do I formulate the condition "broadcast received" ?
Ok explanations
Robotium Solo is an instrumentation framework with nice api
It has a method called "solo.waitForCondition(Condition, int timeout)"
I want to formulate (the word formulate means say what i want to say in correct words)
the correct condition that will tell me that the broadcast was indeed received
I want to write some code (I don't know which exactly) to know that the broadcast was indeed sent
for example, if i want to know that a button is now visible i would write
solo.waitForCondition(new Condition(){
public boolean isSatisfied(){
Button b = getActivity().findViewById(R.id.myButton);
return b.getVisibility() == View.VISIBLE;
}
}
now back to my question - What (not how, but what) do I write in order to know for sure that the broadcast was sent inside the isSatisfied method
I suppose you meant that you don't want to sleep for 10 seconds, if you get the broadcast earlier. What you can do is
long beginTime = new Date().getTime();
while (new Date().getTime() - beginTime < 10000) {
solo.sleep(500);
if (conditionMet) {
// Do something
break;
}
}
This way you can do these checks on smaller intervals.
Ok, so in fact this is more or less how waitForCondition is implemented. Unfortunately I don't think you can listen for events with robotium. What you can do is monitor the view hierarchy. In your case, there should be some difference to the views that is triggered when the button is clicked, so that is what you need to check for in the Condition (and your example does that).
This is if you don't want to edit the code you are testing. If you are willing to change the code, you can add an onClickListener() and in that you can set a view's Tag to a boolean for example. Later in robotium you can check for that tag for being set. This is however not good way to do it, because you are adding more code just for the sake of the tests.

Categories

Resources